Repository: alibaba/fastjson2
Pull request #3451 (2025-03-26 11:14:29)

fix array multi jsonpath extract NullOnError not work.
### What this PR does / why we need it?

NullOnError feature not work in multi jsonpath extract when using array jsonpath


### Summary of your change
add NullOnError check in `ObjectReaderAdapter` and `JSONPathTypedMultiNames`


#### Please indicate you've done the following:

- [x] Made sure tests are passing and test coverage is added if needed.
- [ ] Made sure commit message follow the rule of [Conventional Commits specification](https://www.conventionalcommits.org/).
- [x] Considered the docs impact and opened a new docs issue or PR with docs changes if needed.


CLAassistant: [![CLA assistant check](https://cla-assistant.io/pull/badge/not_signed)](https://cla-assistant.io/alibaba/fastjson2?pullRequest=3451) <br/>Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our [Contributor License Agreement](https://cla-assistant.io/alibaba/fastjson2?pullRequest=3451) before we can accept your contribution.<br/><sub>You have signed the CLA already but the status is still pending? Let us [recheck](https://cla-assistant.io/check/alibaba/fastjson2?pullRequest=3451) it.</sub>

Patch:
diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONPathTypedMultiNames.java b/core/src/main/java/com/alibaba/fastjson2/JSONPathTypedMultiNames.java
index a985eb1bc..f08172417 100644
--- a/core/src/main/java/com/alibaba/fastjson2/JSONPathTypedMultiNames.java
+++ b/core/src/main/java/com/alibaba/fastjson2/JSONPathTypedMultiNames.java
@@ -113,19 +113,27 @@ class JSONPathTypedMultiNames
                     continue;
                 }
 
-                Object result = fieldWriter.getFieldValue(object);
-
-                Type type = types[i];
-                if (result != null && result.getClass() != type) {
-                    if (type == Long.class) {
-                        result = TypeUtils.toLong(result);
-                    } else if (type == BigDecimal.class) {
-                        result = TypeUtils.toBigDecimal(result);
-                    } else if (type == String[].class) {
-                        result = TypeUtils.toStringArray(result);
-                    } else {
-                        result = TypeUtils.cast(result, type);
+                Object result;
+                try {
+                    result = fieldWriter.getFieldValue(object);
+
+                    Type type = types[i];
+                    if (result != null && result.getClass() != type) {
+                        if (type == Long.class) {
+                            result = TypeUtils.toLong(result);
+                        } else if (type == BigDecimal.class) {
+                            result = TypeUtils.toBigDecimal(result);
+                        } else if (type == String[].class) {
+                            result = TypeUtils.toStringArray(result);
+                        } else {
+                            result = TypeUtils.cast(result, type);
+                        }
                     }
+                } catch (Exception e) {
+                    if (!ignoreError(i)) {
+                        throw new JSONException("jsonpath eval path, path : " + paths[i] + ", msg : " + e.getMessage(), e);
+                    }
+                    result = null;
                 }
                 array[i] = result;
             }
diff --git a/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderAdapter.java b/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderAdapter.java
index 764b0dd53..046b59ca9 100644
--- a/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderAdapter.java
+++ b/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderAdapter.java
@@ -219,7 +219,13 @@ public class ObjectReaderAdapter<T>
 
     public void apply(Consumer<FieldReader> fieldReaderConsumer) {
         for (FieldReader fieldReader : fieldReaders) {
-            fieldReaderConsumer.accept(fieldReader);
+            try {
+                fieldReaderConsumer.accept(fieldReader);
+            } catch (RuntimeException e) {
+                if (!ignoreError(fieldReader)) {
+                    throw e;
+                }
+            }
         }
     }
 
@@ -266,7 +272,14 @@ public class ObjectReaderAdapter<T>
         Object object = creator.get();
 
         for (int i = 0; i < fieldReaders.length; i++) {
-            fieldReaders[i].readFieldValue(jsonReader, object);
+            FieldReader fieldReader = fieldReaders[i];
+            try {
+                fieldReader.readFieldValue(jsonReader, object);
+            } catch (RuntimeException e) {
+                if (!ignoreError(fieldReader)) {
+                    throw e;
+                }
+            }
         }
 
         if (!jsonReader.nextIfArrayEnd()) {
@@ -299,7 +312,13 @@ public class ObjectReaderAdapter<T>
         if (entryCnt == fieldReaders.length) {
             for (int i = 0; i < fieldReaders.length; i++) {
                 FieldReader fieldReader = fieldReaders[i];
-                fieldReader.readFieldValue(jsonReader, object);
+                try {
+                    fieldReader.readFieldValue(jsonReader, object);
+                } catch (RuntimeException e) {
+                    if (!ignoreError(fieldReader)) {
+                        throw e;
+                    }
+                }
             }
         } else {
             readArrayMappingJSONBObject0(jsonReader, object, entryCnt);
@@ -318,7 +337,13 @@ public class ObjectReaderAdapter<T>
                 continue;
             }
             FieldReader fieldReader = fieldReaders[i];
-            fieldReader.readFieldValue(jsonReader, object);
+            try {
+                fieldReader.readFieldValue(jsonReader, object);
+            } catch (RuntimeException e) {
+                if (!ignoreError(fieldReader)) {
+                    throw e;
+                }
+            }
         }
 
         for (int i = fieldReaders.length; i < entryCnt; i++) {
@@ -419,6 +444,10 @@ public class ObjectReaderAdapter<T>
         throw new JSONException("create instance error, " + objectClass, error);
     }
 
+    protected final boolean ignoreError(FieldReader fieldReader) {
+        return (fieldReader.features & JSONReader.Feature.NullOnError.mask) != 0;
+    }
+
     @Override
     public FieldReader getFieldReader(long hashCode) {
         int m = Arrays.binarySearch(hashCodes, hashCode);
@@ -668,28 +697,34 @@ public class ObjectReaderAdapter<T>
                     continue;
                 }
 
-                if (fieldValue.getClass() == fieldReader.fieldType) {
-                    fieldReader.accept(object, fieldValue);
-                } else {
-                    if ((fieldReader instanceof FieldReaderList)
-                            && fieldValue instanceof JSONArray
-                    ) {
-                        ObjectReader objectReader = fieldReader.getObjectReader(provider);
-                        Object fieldValueList = objectReader.createInstance((JSONArray) fieldValue, features);
-                        fieldReader.accept(object, fieldValueList);
-                        continue;
-                    } else if (fieldValue instanceof JSONObject
-                            && fieldReader.fieldType != JSONObject.class
-                    ) {
-                        JSONObject jsonObject = (JSONObject) fieldValue;
-                        Object fieldValueJavaBean = provider
-                                .getObjectReader(fieldReader.fieldType, fieldBased)
-                                .createInstance(jsonObject, features);
-                        fieldReader.accept(object, fieldValueJavaBean);
-                        continue;
+                try {
+                    if (fieldValue.getClass() == fieldReader.fieldType) {
+                        fieldReader.accept(object, fieldValue);
+                    } else {
+                        if ((fieldReader instanceof FieldReaderList)
+                                && fieldValue instanceof JSONArray
+                        ) {
+                            ObjectReader objectReader = fieldReader.getObjectReader(provider);
+                            Object fieldValueList = objectReader.createInstance((JSONArray) fieldValue, features);
+                            fieldReader.accept(object, fieldValueList);
+                            continue;
+                        } else if (fieldValue instanceof JSONObject
+                                && fieldReader.fieldType != JSONObject.class
+                        ) {
+                            JSONObject jsonObject = (JSONObject) fieldValue;
+                            Object fieldValueJavaBean = provider
+                                    .getObjectReader(fieldReader.fieldType, fieldBased)
+                                    .createInstance(jsonObject, features);
+                            fieldReader.accept(object, fieldValueJavaBean);
+                            continue;
+                        }
+
+                        fieldReader.acceptAny(object, fieldValue, features);
+                    }
+                } catch (RuntimeException e) {
+                    if (!ignoreError(fieldReader)) {
+                        throw e;
                     }
-
-                    fieldReader.acceptAny(object, fieldValue, features);
                 }
             }
         } else {


Test patch:
diff --git a/core/src/test/java/com/alibaba/fastjson2/jsonpath/JSONPathTypedMultiNamesTest.java b/core/src/test/java/com/alibaba/fastjson2/jsonpath/JSONPathTypedMultiNamesTest.java
index fe6c102f3..c45837777 100644
--- a/core/src/test/java/com/alibaba/fastjson2/jsonpath/JSONPathTypedMultiNamesTest.java
+++ b/core/src/test/java/com/alibaba/fastjson2/jsonpath/JSONPathTypedMultiNamesTest.java
@@ -86,6 +86,24 @@ public class JSONPathTypedMultiNamesTest {
         assertThrows(JSONException.class, () -> jsonPath.extract("["));
     }
 
+    @Test
+    public void testArrayNullOnError() {
+        JSONPath jsonPath = JSONPath.of(
+                new String[]{"$.a[0].x0", "$.a[0].x1"},
+                new Type[]{Long.class, String.class},
+                null,
+                new long[]{
+                        NullOnError.mask,
+                        NullOnError.mask
+                },
+                ZoneId.systemDefault()
+        );
+
+        Object[] result = (Object[]) jsonPath.extract("{\"a\":[{\"x0\":\"xx\",\"x1\":\"xx\"}]}");
+        assertNull(result[0]);
+        assertNotNull(result[1]);
+    }
+
     static class Bean {
         int f0;
         int f1;