diff --git a/benchmark/src/test/java/com/alibaba/fastjson2/benchmark/jsonpath/JSONPathMultiTest2.java b/benchmark/src/test/java/com/alibaba/fastjson2/benchmark/jsonpath/JSONPathMultiTest2.java index e99216628a..270f9c4a97 100644 --- a/benchmark/src/test/java/com/alibaba/fastjson2/benchmark/jsonpath/JSONPathMultiTest2.java +++ b/benchmark/src/test/java/com/alibaba/fastjson2/benchmark/jsonpath/JSONPathMultiTest2.java @@ -41,11 +41,12 @@ public static void extract() throws Exception { long millis = System.currentTimeMillis() - start; System.out.println("JSONPathMultiBenchmark2-extract millis : " + millis); } - /// zulu8.62.0.19 : 792 + /// zulu8.62.0.19 : 792 541 } public static void main(String[] args) throws Exception { // eval(); - evalMulti(); +// evalMulti(); + extract(); } } diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONPathTypedMultiNames.java b/core/src/main/java/com/alibaba/fastjson2/JSONPathTypedMultiNames.java index bb3a02c318..a985eb1bcd 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONPathTypedMultiNames.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONPathTypedMultiNames.java @@ -1,7 +1,7 @@ package com.alibaba.fastjson2; import com.alibaba.fastjson2.reader.FieldReader; -import com.alibaba.fastjson2.reader.ObjectReaderCreator; +import com.alibaba.fastjson2.reader.ObjectReaderAdapter; import com.alibaba.fastjson2.util.TypeUtils; import com.alibaba.fastjson2.writer.FieldWriter; import com.alibaba.fastjson2.writer.ObjectWriter; @@ -9,7 +9,6 @@ import java.lang.reflect.Type; import java.math.BigDecimal; import java.time.ZoneId; -import java.util.Arrays; import java.util.Map; class JSONPathTypedMultiNames @@ -17,9 +16,8 @@ class JSONPathTypedMultiNames final JSONPath prefix; final JSONPath[] namePaths; final String[] names; - final long[] hashCodes; - final short[] mapping; final FieldReader[] fieldReaders; + final ObjectReaderAdapter objectReader; JSONPathTypedMultiNames( JSONPath[] paths, @@ -35,50 +33,52 @@ class JSONPathTypedMultiNames this.prefix = prefix; this.namePaths = namePaths; this.names = new String[paths.length]; - - long[] hashCodes = new long[paths.length]; - fieldReaders = new FieldReader[paths.length]; for (int i = 0; i < paths.length; i++) { JSONPathSingleName jsonPathSingleName = (JSONPathSingleName) namePaths[i]; String fieldName = jsonPathSingleName.name; names[i] = fieldName; - hashCodes[i] = jsonPathSingleName.nameHashCode; - String format = formats != null ? formats[i] : null; - - Type fieldType = types[i]; - Class fieldClass = TypeUtils.getClass(fieldType); - - long fieldFeatures = 0; - if (ignoreError(i)) { - fieldFeatures |= JSONReader.Feature.NullOnError.mask; + } + long[] fieldReaderFeatures = new long[names.length]; + if (pathFeatures != null) { + for (int i = 0; i < pathFeatures.length; i++) { + if ((pathFeatures[i] & Feature.NullOnError.mask) != 0) { + fieldReaderFeatures[i] |= JSONReader.Feature.NullOnError.mask; + } } - fieldReaders[i] = ObjectReaderCreator.INSTANCE.createFieldReader( - null, - null, - fieldName, - fieldType, - fieldClass, - i, - fieldFeatures, - format, - null, - null, - null, - null, - null, - null - ); } - this.hashCodes = Arrays.copyOf(hashCodes, hashCodes.length); - Arrays.sort(this.hashCodes); - - mapping = new short[this.hashCodes.length]; - for (int i = 0; i < hashCodes.length; i++) { - long hashCode = hashCodes[i]; - int index = Arrays.binarySearch(this.hashCodes, hashCode); - mapping[index] = (short) i; + Type[] fieldTypes = types.clone(); + for (int i = 0; i < fieldTypes.length; i++) { + Type fieldType = fieldTypes[i]; + if (fieldType == boolean.class) { + fieldTypes[i] = Boolean.class; + } else if (fieldType == char.class) { + fieldTypes[i] = Character.class; + } else if (fieldType == byte.class) { + fieldTypes[i] = Byte.class; + } else if (fieldType == short.class) { + fieldTypes[i] = Short.class; + } else if (fieldType == int.class) { + fieldTypes[i] = Integer.class; + } else if (fieldType == long.class) { + fieldTypes[i] = Long.class; + } else if (fieldType == float.class) { + fieldTypes[i] = Float.class; + } else if (fieldType == double.class) { + fieldTypes[i] = Double.class; + } } + + final int length = names.length; + objectReader = (ObjectReaderAdapter) JSONFactory.getDefaultObjectReaderProvider() + .createObjectReader( + names, + fieldTypes, + fieldReaderFeatures, + () -> new Object[length], + (o, i, v) -> o[i] = v + ); + this.fieldReaders = objectReader.getFieldReaders(); } @Override @@ -100,23 +100,7 @@ public Object eval(Object root) { } if (object instanceof Map) { - Map map = (Map) object; - for (int i = 0; i < names.length; i++) { - Object result = map.get(names[i]); - 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); - } - } - array[i] = result; - } + return objectReader.createInstance((Map) object, 0); } else { ObjectWriter objectReader = JSONFactory.defaultObjectWriterProvider .getObjectWriter( @@ -165,30 +149,6 @@ public Object extract(JSONReader jsonReader) { throw new JSONException(jsonReader.info("illegal input, expect '[', but " + jsonReader.current())); } - Object[] values = new Object[paths.length]; - while (!jsonReader.nextIfObjectEnd()) { - long nameHashCode = jsonReader.readFieldNameHashCode(); - - int m = Arrays.binarySearch(hashCodes, nameHashCode); - if (m < 0) { - jsonReader.skipValue(); - continue; - } - - int index = this.mapping[m]; - FieldReader fieldReader = fieldReaders[index]; - Object fieldValue; - try { - fieldValue = fieldReader.readFieldValue(jsonReader); - } catch (Exception e) { - if (!ignoreError(index)) { - throw e; - } - fieldValue = null; - } - values[index] = fieldValue; - } - - return values; + return objectReader.readObject(jsonReader, null, null, 0); } } diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONPathTypedMultiNamesPrefixIndex1.java b/core/src/main/java/com/alibaba/fastjson2/JSONPathTypedMultiNamesPrefixIndex1.java index 1df43f7803..db791440ad 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONPathTypedMultiNamesPrefixIndex1.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONPathTypedMultiNamesPrefixIndex1.java @@ -1,10 +1,7 @@ package com.alibaba.fastjson2; -import com.alibaba.fastjson2.reader.FieldReader; - import java.lang.reflect.Type; import java.time.ZoneId; -import java.util.Arrays; public class JSONPathTypedMultiNamesPrefixIndex1 extends JSONPathTypedMultiNames { @@ -53,34 +50,6 @@ public Object extract(JSONReader jsonReader) { return new Object[paths.length]; } - if (!jsonReader.nextIfObjectStart()) { - throw new JSONException(jsonReader.info("illegal input, expect '[', but " + jsonReader.current())); - } - - Object[] values = new Object[paths.length]; - while (!jsonReader.nextIfObjectEnd()) { - long nameHashCode = jsonReader.readFieldNameHashCode(); - - int m = Arrays.binarySearch(hashCodes, nameHashCode); - if (m < 0) { - jsonReader.skipValue(); - continue; - } - - int index = this.mapping[m]; - FieldReader fieldReader = fieldReaders[index]; - Object fieldValue; - try { - fieldValue = fieldReader.readFieldValue(jsonReader); - } catch (Exception e) { - if (!ignoreError(index)) { - throw e; - } - fieldValue = null; - } - values[index] = fieldValue; - } - - return values; + return objectReader.readObject(jsonReader); } } diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONPathTypedMultiNamesPrefixName1.java b/core/src/main/java/com/alibaba/fastjson2/JSONPathTypedMultiNamesPrefixName1.java index d78132119f..acacc85488 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONPathTypedMultiNamesPrefixName1.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONPathTypedMultiNamesPrefixName1.java @@ -1,10 +1,7 @@ package com.alibaba.fastjson2; -import com.alibaba.fastjson2.reader.FieldReader; - import java.lang.reflect.Type; import java.time.ZoneId; -import java.util.Arrays; public class JSONPathTypedMultiNamesPrefixName1 extends JSONPathTypedMultiNames { @@ -62,30 +59,6 @@ public Object extract(JSONReader jsonReader) { throw new JSONException(jsonReader.info("illegal input, expect '[', but " + jsonReader.current())); } - Object[] values = new Object[paths.length]; - while (!jsonReader.nextIfObjectEnd()) { - long nameHashCode = jsonReader.readFieldNameHashCode(); - - int m = Arrays.binarySearch(hashCodes, nameHashCode); - if (m < 0) { - jsonReader.skipValue(); - continue; - } - - int index = this.mapping[m]; - FieldReader fieldReader = fieldReaders[index]; - Object fieldValue; - try { - fieldValue = fieldReader.readFieldValue(jsonReader); - } catch (Exception e) { - if (!ignoreError(index)) { - throw e; - } - fieldValue = null; - } - values[index] = fieldValue; - } - - return values; + return objectReader.readObject(jsonReader); } } diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONPathTypedMultiNamesPrefixName2.java b/core/src/main/java/com/alibaba/fastjson2/JSONPathTypedMultiNamesPrefixName2.java index 1ade81374e..c22a1415a5 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONPathTypedMultiNamesPrefixName2.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONPathTypedMultiNamesPrefixName2.java @@ -2,7 +2,6 @@ import java.lang.reflect.Type; import java.time.ZoneId; -import java.util.Arrays; public class JSONPathTypedMultiNamesPrefixName2 extends JSONPathTypedMultiNames { @@ -89,40 +88,7 @@ public Object extract(JSONReader jsonReader) { return new Object[paths.length]; } - if (!jsonReader.nextIfObjectStart()) { - throw error(jsonReader); - } - - Object[] values = new Object[paths.length]; - while (!jsonReader.nextIfObjectEnd()) { - if (jsonReader.isEnd()) { - throw error(jsonReader); - } - - int m = Arrays.binarySearch( - hashCodes, - jsonReader.readFieldNameHashCode() - ); - - if (m < 0) { - jsonReader.skipValue(); - continue; - } - - int index = this.mapping[m]; - Object fieldValue; - try { - fieldValue = fieldReaders[index].readFieldValue(jsonReader); - } catch (Exception e) { - if (!ignoreError(index)) { - throw e; - } - fieldValue = null; - } - values[index] = fieldValue; - } - - return values; + return objectReader.readObject(jsonReader); } private static JSONException error(JSONReader jsonReader) { diff --git a/core/src/main/java/com/alibaba/fastjson2/reader/FieldReaderDate.java b/core/src/main/java/com/alibaba/fastjson2/reader/FieldReaderDate.java index e5e2d0129b..aeb0e216fc 100644 --- a/core/src/main/java/com/alibaba/fastjson2/reader/FieldReaderDate.java +++ b/core/src/main/java/com/alibaba/fastjson2/reader/FieldReaderDate.java @@ -59,7 +59,15 @@ protected void acceptNull(T object) { @Override public void readFieldValue(JSONReader jsonReader, T object) { - Date date = (Date) dateReader.readObject(jsonReader, fieldType, fieldName, features); + Date date; + try { + date = (Date) dateReader.readObject(jsonReader, fieldType, fieldName, features); + } catch (Exception e) { + if ((features & JSONReader.Feature.NullOnError.mask) == 0) { + throw e; + } + date = null; + } accept(object, date); } diff --git a/core/src/main/java/com/alibaba/fastjson2/reader/FieldReaderObject.java b/core/src/main/java/com/alibaba/fastjson2/reader/FieldReaderObject.java index b5bb3ab96a..5dc79a91d4 100644 --- a/core/src/main/java/com/alibaba/fastjson2/reader/FieldReaderObject.java +++ b/core/src/main/java/com/alibaba/fastjson2/reader/FieldReaderObject.java @@ -123,8 +123,8 @@ public void readFieldValue(JSONReader jsonReader, T object) { return; } + Object value; try { - Object value; if (jsonReader.nextIfNull()) { if (fieldClass == OptionalInt.class) { value = OptionalInt.empty(); @@ -151,22 +151,26 @@ public void readFieldValue(JSONReader jsonReader, T object) { } else { value = objectReader.readObject(jsonReader, fieldType, fieldName, features); } - accept(object, value); - - if (noneStaticMemberClass) { - BeanUtils.setNoneStaticMemberClassParent(value, object); - } } catch (JSONSchemaValidException ex) { throw ex; } catch (Exception | IllegalAccessError ex) { - Member member = this.field != null ? this.field : this.method; - String message; - if (member != null) { - message = "read field '" + member.getDeclaringClass().getName() + "." + member.getName(); - } else { - message = "read field " + fieldName + " error"; + if ((features & JSONReader.Feature.NullOnError.mask) == 0) { + Member member = this.field != null ? this.field : this.method; + String message; + if (member != null) { + message = "read field '" + member.getDeclaringClass().getName() + "." + member.getName(); + } else { + message = "read field " + fieldName + " error"; + } + throw new JSONException(jsonReader.info(message), ex); } - throw new JSONException(jsonReader.info(message), ex); + value = null; + } + + accept(object, value); + + if (noneStaticMemberClass) { + BeanUtils.setNoneStaticMemberClassParent(value, object); } } 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 aecac449ce..015fb6cc7a 100644 --- a/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderAdapter.java +++ b/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderAdapter.java @@ -415,6 +415,11 @@ public FieldReader getFieldReader(long hashCode) { return fieldReaders[index]; } + public int getFieldOrdinal(long hashCode) { + int m = Arrays.binarySearch(hashCodes, hashCode); + return m < 0 ? -1 : this.mapping[m]; + } + @Override public FieldReader getFieldReaderLCase(long hashCode) { int m = Arrays.binarySearch(hashCodesLCase, hashCode); diff --git a/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderCreator.java b/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderCreator.java index 88138f07a7..9a4e14d194 100644 --- a/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderCreator.java +++ b/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderCreator.java @@ -2647,6 +2647,16 @@ public FieldReader createFieldReader( return new FieldReaderObjectField(fieldName, fieldType, fieldClass, ordinal, features, format, defaultValue, jsonSchema, field); } + public FieldReader createFieldReader( + String fieldName, + Type fieldType, + Class fieldClass, + long features, + BiConsumer function + ) { + return createFieldReader(null, null, fieldName, fieldType, fieldClass, 0, features, null, null, null, null, null, function, null); + } + public FieldReader createFieldReader( String fieldName, Type fieldType, diff --git a/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderCreatorASM.java b/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderCreatorASM.java index 1750595c1e..fc7a5ded13 100644 --- a/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderCreatorASM.java +++ b/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderCreatorASM.java @@ -604,7 +604,11 @@ private ObjectReaderBean jitObjectReader( return (ObjectReaderBean) constructor .newInstance(objectClass, supplier, fieldReaderArray); } catch (Throwable e) { - throw new JSONException("create objectReader error, objectType " + objectType.getTypeName(), e); + throw new JSONException( + "create objectReader error" + + (objectType == null ? "" : ", objectType " + objectType.getTypeName()), + e + ); } } @@ -2725,6 +2729,15 @@ private int genReadFieldValue( String format = fieldReader.format; Type itemType = fieldReader.itemType; + if ((fieldFeatures & JSONReader.Feature.NullOnError.mask) != 0) { + mw.visitVarInsn(Opcodes.ALOAD, THIS); + mw.visitFieldInsn(Opcodes.GETFIELD, classNameType, fieldReader(i), DESC_FIELD_READER); + mw.visitVarInsn(Opcodes.ALOAD, JSON_READER); + mw.visitVarInsn(Opcodes.ALOAD, OBJECT); + mw.visitMethodInsn(Opcodes.INVOKEVIRTUAL, TYPE_FIELD_READE, "readFieldValue", METHOD_DESC_READ_FIELD_VALUE, false); + return varIndex; + } + Field field = fieldReader.field; Method method = fieldReader.method; @@ -2850,7 +2863,7 @@ private int genReadFieldValue( mw.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z", false); mw.visitJumpInsn(Opcodes.IFEQ, addResolveTask_); - if (fieldClass.isAssignableFrom(objectClass)) { + if (objectClass != null && fieldClass.isAssignableFrom(objectClass)) { mw.visitVarInsn(Opcodes.ALOAD, OBJECT); // mw.visitTypeInsn(CHECKCAST, TYPE_FIELD_CLASS); // cast mw.visitJumpInsn(Opcodes.GOTO, endObject_); @@ -2980,7 +2993,8 @@ private int genReadFieldValue( ); } - if (method != null || (Modifier.isPublic(objectClass.getModifiers()) + if (method != null + || ((objectClass == null || Modifier.isPublic(objectClass.getModifiers())) && Modifier.isPublic(fieldModifier) && !Modifier.isFinal(fieldModifier) && !classLoader.isExternalClass(objectClass)) diff --git a/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderProvider.java b/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderProvider.java index d9752fd9d0..78910e2a6c 100644 --- a/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderProvider.java +++ b/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderProvider.java @@ -1131,12 +1131,29 @@ public ObjectReader createObjectReader( Type[] types, Supplier supplier, FieldConsumer c + ) { + return createObjectReader(names, types, null, supplier, c); + } + + public ObjectReader createObjectReader( + String[] names, + Type[] types, + long[] features, + Supplier supplier, + FieldConsumer c ) { FieldReader[] fieldReaders = new FieldReader[names.length]; for (int i = 0; i < names.length; i++) { Type fieldType = types[i]; Class fieldClass = TypeUtils.getClass(fieldType); - fieldReaders[i] = ObjectReaders.fieldReader(names[i], fieldType, fieldClass, new FieldBiConsumer(i, c)); + long feature = features != null && i < features.length ? features[i] : 0; + fieldReaders[i] = creator.createFieldReader( + names[i], + fieldType, + fieldClass, + feature, + new FieldBiConsumer(i, c) + ); } return creator.createObjectReader(