();
+
+ final Reader r;
+
+ private char[] buf;
+ private int end = -1;
+ private int readCount = 0;
+
+ ReaderValidator(Reader r) {
+ this.r = r;
+ buf = bufLocal.get();
+ if (buf != null) {
+ bufLocal.set(null);
+ } else {
+ buf = new char[1024 * 8];
+ }
+
+ next();
+ skipWhiteSpace();
+ }
+
+ void next() {
+ if (pos < end) {
+ ch = buf[++pos];
+ } else {
+ if (!eof) {
+ int len;
+ try {
+ len = r.read(buf, 0, buf.length);
+ readCount++;
+ } catch (IOException ex) {
+ throw new JSONException("read error");
+ }
+
+ if (len > 0) {
+ ch = buf[0];
+ pos = 0;
+ end = len - 1;
+ }
+ else if (len == -1) {
+ pos = 0;
+ end = 0;
+ buf = null;
+ ch = '\0';
+ eof = true;
+ } else {
+ pos = 0;
+ end = 0;
+ buf = null;
+ ch = '\0';
+ eof = true;
+ throw new JSONException("read error");
+ }
+ }
+ }
+ }
+
+ public void close() throws IOException {
+ bufLocal.set(buf);
+ r.close();
+ }
+ }
+}
diff --git a/src/main/java/com/alibaba/fastjson/JSONWriter.java b/src/main/java/com/alibaba/fastjson/JSONWriter.java
old mode 100755
new mode 100644
index 7cb288dd8e..d5d72f6d6d
--- a/src/main/java/com/alibaba/fastjson/JSONWriter.java
+++ b/src/main/java/com/alibaba/fastjson/JSONWriter.java
@@ -53,13 +53,13 @@ public void writeObject(String object) {
serializer.write(object);
- afterWriter();
+ afterWrite();
}
public void writeObject(Object object) {
beforeWrite();
serializer.write(object);
- afterWriter();
+ afterWrite();
}
public void startArray() {
@@ -145,7 +145,7 @@ private void beforeWrite() {
}
}
- private void afterWriter() {
+ private void afterWrite() {
if (context == null) {
return;
}
diff --git a/src/main/java/com/alibaba/fastjson/PropertyNamingStrategy.java b/src/main/java/com/alibaba/fastjson/PropertyNamingStrategy.java
index adfa285bbe..7d7f416f23 100644
--- a/src/main/java/com/alibaba/fastjson/PropertyNamingStrategy.java
+++ b/src/main/java/com/alibaba/fastjson/PropertyNamingStrategy.java
@@ -4,10 +4,12 @@
* @since 1.2.15
*/
public enum PropertyNamingStrategy {
- CamelCase, //
- PascalCase, //
- SnakeCase, //
- KebabCase;
+ CamelCase, // camelCase
+ PascalCase, // PascalCase
+ SnakeCase, // snake_case
+ KebabCase, // kebab-case
+ NoChange, //
+ NeverUseThisValueExceptDefaultValue;
public String translate(String propertyName) {
switch (this) {
@@ -63,6 +65,8 @@ public String translate(String propertyName) {
return propertyName;
}
+ case NoChange:
+ case NeverUseThisValueExceptDefaultValue:
default:
return propertyName;
}
diff --git a/src/main/java/com/alibaba/fastjson/TypeReference.java b/src/main/java/com/alibaba/fastjson/TypeReference.java
old mode 100755
new mode 100644
index 11b09809b5..bf21cb5d75
--- a/src/main/java/com/alibaba/fastjson/TypeReference.java
+++ b/src/main/java/com/alibaba/fastjson/TypeReference.java
@@ -77,6 +77,11 @@ protected TypeReference(Type... actualTypeArguments){
argTypes[i] = TypeUtils.checkPrimitiveArray(
(GenericArrayType) argTypes[i]);
}
+
+ // 如果有多层泛型且该泛型已经注明实现的情况下,判断该泛型下一层是否还有泛型
+ if(argTypes[i] instanceof ParameterizedType) {
+ argTypes[i] = handlerParameterizedType((ParameterizedType) argTypes[i], actualTypeArguments, actualIndex);
+ }
}
Type key = new ParameterizedTypeImpl(argTypes, thisClass, rawType);
@@ -87,7 +92,42 @@ protected TypeReference(Type... actualTypeArguments){
}
type = cachedType;
+ }
+
+ public static Type intern(ParameterizedTypeImpl type) {
+ Type cachedType = classTypeCache.get(type);
+ if (cachedType == null) {
+ classTypeCache.putIfAbsent(type, type);
+ cachedType = classTypeCache.get(type);
+ }
+
+ return cachedType;
+ }
+
+ private Type handlerParameterizedType(ParameterizedType type, Type[] actualTypeArguments, int actualIndex) {
+ Class> thisClass = this.getClass();
+ Type rawType = type.getRawType();
+ Type[] argTypes = type.getActualTypeArguments();
+ for(int i = 0; i < argTypes.length; ++i) {
+ if (argTypes[i] instanceof TypeVariable && actualIndex < actualTypeArguments.length) {
+ argTypes[i] = actualTypeArguments[actualIndex++];
+ }
+
+ // fix for openjdk and android env
+ if (argTypes[i] instanceof GenericArrayType) {
+ argTypes[i] = TypeUtils.checkPrimitiveArray(
+ (GenericArrayType) argTypes[i]);
+ }
+
+ // 如果有多层泛型且该泛型已经注明实现的情况下,判断该泛型下一层是否还有泛型
+ if(argTypes[i] instanceof ParameterizedType) {
+ argTypes[i] = handlerParameterizedType((ParameterizedType) argTypes[i], actualTypeArguments, actualIndex);
+ }
+ }
+
+ Type key = new ParameterizedTypeImpl(argTypes, thisClass, rawType);
+ return key;
}
/**
diff --git a/src/main/java/com/alibaba/fastjson/annotation/JSONCreator.java b/src/main/java/com/alibaba/fastjson/annotation/JSONCreator.java
old mode 100755
new mode 100644
diff --git a/src/main/java/com/alibaba/fastjson/annotation/JSONField.java b/src/main/java/com/alibaba/fastjson/annotation/JSONField.java
old mode 100755
new mode 100644
index 9907c9ea86..e859d76924
--- a/src/main/java/com/alibaba/fastjson/annotation/JSONField.java
+++ b/src/main/java/com/alibaba/fastjson/annotation/JSONField.java
@@ -79,4 +79,11 @@
* @since 1.2.31
*/
boolean unwrapped() default false;
+
+ /**
+ * Only support Object
+ *
+ * @since 1.2.61
+ */
+ String defaultValue() default "";
}
diff --git a/src/main/java/com/alibaba/fastjson/annotation/JSONPOJOBuilder.java b/src/main/java/com/alibaba/fastjson/annotation/JSONPOJOBuilder.java
index 648065dbc0..7e708294dd 100644
--- a/src/main/java/com/alibaba/fastjson/annotation/JSONPOJOBuilder.java
+++ b/src/main/java/com/alibaba/fastjson/annotation/JSONPOJOBuilder.java
@@ -21,7 +21,7 @@
*
* Default value is "build".
*/
- public String buildMethod() default "build";
+ String buildMethod() default "build";
/**
* Property used for (re)defining name prefix to use for
@@ -37,6 +37,6 @@
* would be used for binding JSON property "value" (using type
* indicated by the argument; or one defined with annotations.
*/
- public String withPrefix() default "with";
+ String withPrefix() default "with";
}
diff --git a/src/main/java/com/alibaba/fastjson/annotation/JSONType.java b/src/main/java/com/alibaba/fastjson/annotation/JSONType.java
old mode 100755
new mode 100644
index fd179447e0..39f1fe6e04
--- a/src/main/java/com/alibaba/fastjson/annotation/JSONType.java
+++ b/src/main/java/com/alibaba/fastjson/annotation/JSONType.java
@@ -7,6 +7,8 @@
import com.alibaba.fastjson.PropertyNamingStrategy;
import com.alibaba.fastjson.parser.Feature;
+import com.alibaba.fastjson.parser.ParserConfig;
+import com.alibaba.fastjson.serializer.SerializeFilter;
import com.alibaba.fastjson.serializer.SerializerFeature;
/**
@@ -63,5 +65,16 @@
boolean serializeEnumAsJavaBean() default false;
- PropertyNamingStrategy naming() default PropertyNamingStrategy.CamelCase;
+ PropertyNamingStrategy naming() default PropertyNamingStrategy.NeverUseThisValueExceptDefaultValue;
+
+ /**
+ * @since 1.2.49
+ */
+ Class extends SerializeFilter>[] serialzeFilters() default {};
+
+ /**
+ * @since 1.2.71
+ * @return
+ */
+ Class extends ParserConfig.AutoTypeCheckHandler> autoTypeCheckHandler() default ParserConfig.AutoTypeCheckHandler.class;
}
diff --git a/src/main/java/com/alibaba/fastjson/asm/ByteVector.java b/src/main/java/com/alibaba/fastjson/asm/ByteVector.java
old mode 100755
new mode 100644
index 72d2b1519a..2d437ea92b
--- a/src/main/java/com/alibaba/fastjson/asm/ByteVector.java
+++ b/src/main/java/com/alibaba/fastjson/asm/ByteVector.java
@@ -30,194 +30,203 @@
package com.alibaba.fastjson.asm;
/**
- * A dynamically extensible vector of bytes. This class is roughly equivalent to a DataOutputStream on top of a
- * ByteArrayOutputStream, but is more efficient.
- *
+ * A dynamically extensible vector of bytes. This class is roughly equivalent to
+ * a DataOutputStream on top of a ByteArrayOutputStream, but is more efficient.
+ *
* @author Eric Bruneton
*/
public class ByteVector {
- /**
- * The content of this vector.
- */
- byte[] data;
+ /**
+ * The content of this vector.
+ */
+ public byte[] data;
- /**
- * Actual number of bytes in this vector.
- */
- int length;
+ /**
+ * Actual number of bytes in this vector.
+ */
+ public int length;
- /**
- * Constructs a new {@link ByteVector ByteVector} with a default initial size.
- */
- public ByteVector(){
- data = new byte[64];
- }
+ /**
+ * Constructs a new {@link ByteVector ByteVector} with a default initial size.
+ */
+ public ByteVector() {
+ data = new byte[64];
+ }
- /**
- * Constructs a new {@link ByteVector ByteVector} with the given initial size.
- *
- * @param initialSize the initial size of the byte vector to be constructed.
- */
- public ByteVector(final int initialSize){
- data = new byte[initialSize];
- }
+ /**
+ * Constructs a new {@link ByteVector ByteVector} with the given initial size.
+ *
+ * @param initialSize the initial size of the byte vector to be constructed.
+ */
+ public ByteVector(final int initialSize) {
+ data = new byte[initialSize];
+ }
- /**
- * Puts a byte into this byte vector. The byte vector is automatically enlarged if necessary.
- *
- * @param b a byte.
- * @return this byte vector.
- */
- public ByteVector putByte(final int b) {
- int length = this.length;
- if (length + 1 > data.length) {
- enlarge(1);
- }
- data[length++] = (byte) b;
- this.length = length;
- return this;
- }
+ /**
+ * Puts a byte into this byte vector. The byte vector is automatically enlarged
+ * if necessary.
+ *
+ * @param b a byte.
+ * @return this byte vector.
+ */
+ public ByteVector putByte(final int b) {
+ int length = this.length;
+ if (length + 1 > data.length) {
+ enlarge(1);
+ }
+ data[length++] = (byte) b;
+ this.length = length;
+ return this;
+ }
- /**
- * Puts two bytes into this byte vector. The byte vector is automatically enlarged if necessary.
- *
- * @param b1 a byte.
- * @param b2 another byte.
- * @return this byte vector.
- */
- ByteVector put11(final int b1, final int b2) {
- int length = this.length;
- if (length + 2 > data.length) {
- enlarge(2);
- }
- byte[] data = this.data;
- data[length++] = (byte) b1;
- data[length++] = (byte) b2;
- this.length = length;
- return this;
- }
+ /**
+ * Puts two bytes into this byte vector. The byte vector is automatically
+ * enlarged if necessary.
+ *
+ * @param b1 a byte.
+ * @param b2 another byte.
+ * @return this byte vector.
+ */
+ ByteVector put11(final int b1, final int b2) {
+ int length = this.length;
+ if (length + 2 > data.length) {
+ enlarge(2);
+ }
+ final byte[] data = this.data;
+ data[length++] = (byte) b1;
+ data[length++] = (byte) b2;
+ this.length = length;
+ return this;
+ }
- /**
- * Puts a short into this byte vector. The byte vector is automatically enlarged if necessary.
- *
- * @param s a short.
- * @return this byte vector.
- */
- public ByteVector putShort(final int s) {
- int length = this.length;
- if (length + 2 > data.length) {
- enlarge(2);
- }
- byte[] data = this.data;
- data[length++] = (byte) (s >>> 8);
- data[length++] = (byte) s;
- this.length = length;
- return this;
- }
+ /**
+ * Puts a short into this byte vector. The byte vector is automatically enlarged
+ * if necessary.
+ *
+ * @param s a short.
+ * @return this byte vector.
+ */
+ public ByteVector putShort(final int s) {
+ int length = this.length;
+ if (length + 2 > data.length) {
+ enlarge(2);
+ }
+ final byte[] data = this.data;
+ data[length++] = (byte) (s >>> 8);
+ data[length++] = (byte) s;
+ this.length = length;
+ return this;
+ }
- /**
- * Puts a byte and a short into this byte vector. The byte vector is automatically enlarged if necessary.
- *
- * @param b a byte.
- * @param s a short.
- * @return this byte vector.
- */
- ByteVector put12(final int b, final int s) {
- int length = this.length;
- if (length + 3 > data.length) {
- enlarge(3);
- }
- byte[] data = this.data;
- data[length++] = (byte) b;
- data[length++] = (byte) (s >>> 8);
- data[length++] = (byte) s;
- this.length = length;
- return this;
- }
+ /**
+ * Puts a byte and a short into this byte vector. The byte vector is
+ * automatically enlarged if necessary.
+ *
+ * @param b a byte.
+ * @param s a short.
+ * @return this byte vector.
+ */
+ public ByteVector put12(final int b, final int s) {
+ int length = this.length;
+ if (length + 3 > data.length) {
+ enlarge(3);
+ }
+ final byte[] data = this.data;
+ data[length++] = (byte) b;
+ data[length++] = (byte) (s >>> 8);
+ data[length++] = (byte) s;
+ this.length = length;
+ return this;
+ }
- /**
- * Puts an int into this byte vector. The byte vector is automatically enlarged if necessary.
- *
- * @param i an int.
- * @return this byte vector.
- */
- public ByteVector putInt(final int i) {
- int length = this.length;
- if (length + 4 > data.length) {
- enlarge(4);
- }
- byte[] data = this.data;
- data[length++] = (byte) (i >>> 24);
- data[length++] = (byte) (i >>> 16);
- data[length++] = (byte) (i >>> 8);
- data[length++] = (byte) i;
- this.length = length;
- return this;
- }
+ /**
+ * Puts an int into this byte vector. The byte vector is automatically enlarged
+ * if necessary.
+ *
+ * @param i an int.
+ * @return this byte vector.
+ */
+ public ByteVector putInt(final int i) {
+ int length = this.length;
+ if (length + 4 > data.length) {
+ enlarge(4);
+ }
+ final byte[] data = this.data;
+ data[length++] = (byte) (i >>> 24);
+ data[length++] = (byte) (i >>> 16);
+ data[length++] = (byte) (i >>> 8);
+ data[length++] = (byte) i;
+ this.length = length;
+ return this;
+ }
- /**
- * Puts an UTF8 string into this byte vector. The byte vector is automatically enlarged if necessary.
- *
- * @param s a String.
- * @return this byte vector.
- */
- public ByteVector putUTF8(final String s) {
- int charLength = s.length();
- int len = length;
- if (len + 2 + charLength > data.length) {
- enlarge(2 + charLength);
- }
- byte[] data = this.data;
- // optimistic algorithm: instead of computing the byte length and then
- // serializing the string (which requires two loops), we assume the byte
- // length is equal to char length (which is the most frequent case), and
- // we start serializing the string right away. During the serialization,
- // if we find that this assumption is wrong, we continue with the
- // general method.
- data[len++] = (byte) (charLength >>> 8);
- data[len++] = (byte) charLength;
- for (int i = 0; i < charLength; ++i) {
- char c = s.charAt(i);
- if (c >= '\001' && c <= '\177') {
- data[len++] = (byte) c;
- } else {
- throw new UnsupportedOperationException();
- }
- }
- length = len;
- return this;
- }
+ /**
+ * Puts an UTF8 string into this byte vector. The byte vector is automatically
+ * enlarged if necessary.
+ *
+ * @param s a String.
+ * @return this byte vector.
+ */
+ public ByteVector putUTF8(final String s) {
+ final int charLength = s.length();
+ int len = length;
+ if (len + 2 + charLength > data.length) {
+ enlarge(2 + charLength);
+ }
+ final byte[] data = this.data;
+ // optimistic algorithm: instead of computing the byte length and then
+ // serializing the string (which requires two loops), we assume the byte
+ // length is equal to char length (which is the most frequent case), and
+ // we start serializing the string right away. During the serialization,
+ // if we find that this assumption is wrong, we continue with the
+ // general method.
+ data[len++] = (byte) (charLength >>> 8);
+ data[len++] = (byte) charLength;
+ for (int i = 0; i < charLength; ++i) {
+ final char c = s.charAt(i);
+ if ((c >= '\001' && c <= '\177') || (c >= '\u4E00' && c <= '\u9FFF')) {
+ data[len++] = (byte) c;
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
+ length = len;
+ return this;
+ }
- /**
- * Puts an array of bytes into this byte vector. The byte vector is automatically enlarged if necessary.
- *
- * @param b an array of bytes. May be null to put len null bytes into this byte vector.
- * @param off index of the fist byte of b that must be copied.
- * @param len number of bytes of b that must be copied.
- * @return this byte vector.
- */
- public ByteVector putByteArray(final byte[] b, final int off, final int len) {
- if (length + len > data.length) {
- enlarge(len);
- }
- if (b != null) {
- System.arraycopy(b, off, data, length, len);
- }
- length += len;
- return this;
- }
+ /**
+ * Puts an array of bytes into this byte vector. The byte vector is
+ * automatically enlarged if necessary.
+ *
+ * @param b an array of bytes. May be null to put len null
+ * bytes into this byte vector.
+ * @param off index of the fist byte of b that must be copied.
+ * @param len number of bytes of b that must be copied.
+ * @return this byte vector.
+ */
+ public ByteVector putByteArray(final byte[] b, final int off, final int len) {
+ if (length + len > data.length) {
+ enlarge(len);
+ }
+ if (b != null) {
+ System.arraycopy(b, off, data, length, len);
+ }
+ length += len;
+ return this;
+ }
- /**
- * Enlarge this byte vector so that it can receive n more bytes.
- *
- * @param size number of additional bytes that this byte vector should be able to receive.
- */
- private void enlarge(final int size) {
- int length1 = 2 * data.length;
- int length2 = length + size;
- byte[] newData = new byte[length1 > length2 ? length1 : length2];
- System.arraycopy(data, 0, newData, 0, length);
- data = newData;
- }
+ /**
+ * Enlarge this byte vector so that it can receive n more bytes.
+ *
+ * @param size number of additional bytes that this byte vector should be able
+ * to receive.
+ */
+ private void enlarge(final int size) {
+ final int length1 = 2 * data.length;
+ final int length2 = length + size;
+ final byte[] newData = new byte[length1 > length2 ? length1 : length2];
+ System.arraycopy(data, 0, newData, 0, length);
+ data = newData;
+ }
}
diff --git a/src/main/java/com/alibaba/fastjson/asm/ClassReader.java b/src/main/java/com/alibaba/fastjson/asm/ClassReader.java
index fdab09f1a8..11cb6f3706 100644
--- a/src/main/java/com/alibaba/fastjson/asm/ClassReader.java
+++ b/src/main/java/com/alibaba/fastjson/asm/ClassReader.java
@@ -8,14 +8,16 @@
* Created by wenshao on 05/08/2017.
*/
public class ClassReader {
- public final byte[] b;
+ public final byte[] b;
private final int[] items;
private final String[] strings;
private final int maxStringLength;
- public final int header;
+ public final int header;
+ private boolean readAnnotations;
+ public ClassReader(InputStream is, boolean readAnnotations) throws IOException {
+ this.readAnnotations = readAnnotations;
- public ClassReader(final InputStream is) throws IOException {
{
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
@@ -81,18 +83,26 @@ public ClassReader(final InputStream is) throws IOException {
public void accept(final TypeCollector classVisitor) {
char[] c = new char[maxStringLength]; // buffer used to read strings
- int i, j, k; // loop variables
- int u, v, w; // indexes in b
-
- String attrName;
+ int i, j; // loop variables
+ int u, v; // indexes in b
int anns = 0;
- int ianns = 0;
+
+ //read annotations
+ if (readAnnotations) {
+ u = getAttributes();
+ for (i = readUnsignedShort(u); i > 0; --i) {
+ String attrName = readUTF8(u + 2, c);
+ if ("RuntimeVisibleAnnotations".equals(attrName)) {
+ anns = u + 8;
+ break;
+ }
+ u += 6 + readInt(u + 4);
+ }
+ }
// visits the header
u = header;
- v = items[readUnsignedShort(u + 4)];
int len = readUnsignedShort(u + 6);
- w = 0;
u += 8;
for (i = 0; i < len; ++i) {
u += 2;
@@ -123,7 +133,12 @@ public void accept(final TypeCollector classVisitor) {
v += 6 + readInt(v + 2);
}
- //annotations not needed.
+ if (anns != 0) {
+ for (i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) {
+ String name = readUTF8(v, c);
+ classVisitor.visitAnnotation(name);
+ }
+ }
// visits the fields
i = readUnsignedShort(u);
@@ -145,6 +160,27 @@ public void accept(final TypeCollector classVisitor) {
}
}
+ private int getAttributes() {
+ // skips the header
+ int u = header + 8 + readUnsignedShort(header + 6) * 2;
+ // skips fields and methods
+ for (int i = readUnsignedShort(u); i > 0; --i) {
+ for (int j = readUnsignedShort(u + 8); j > 0; --j) {
+ u += 6 + readInt(u + 12);
+ }
+ u += 8;
+ }
+ u += 2;
+ for (int i = readUnsignedShort(u); i > 0; --i) {
+ for (int j = readUnsignedShort(u + 8); j > 0; --j) {
+ u += 6 + readInt(u + 12);
+ }
+ u += 8;
+ }
+ // the attribute_info structure starts just after the methods
+ return u + 2;
+ }
+
private int readMethod(TypeCollector classVisitor, char[] c, int u) {
int v;
int w;
@@ -295,5 +331,4 @@ private String readUTF(int index, final int utfLen, final char[] buf) {
}
return new String(buf, 0, strLen);
}
-
}
diff --git a/src/main/java/com/alibaba/fastjson/asm/ClassWriter.java b/src/main/java/com/alibaba/fastjson/asm/ClassWriter.java
old mode 100755
new mode 100644
diff --git a/src/main/java/com/alibaba/fastjson/asm/FieldWriter.java b/src/main/java/com/alibaba/fastjson/asm/FieldWriter.java
old mode 100755
new mode 100644
diff --git a/src/main/java/com/alibaba/fastjson/asm/Item.java b/src/main/java/com/alibaba/fastjson/asm/Item.java
old mode 100755
new mode 100644
diff --git a/src/main/java/com/alibaba/fastjson/asm/Label.java b/src/main/java/com/alibaba/fastjson/asm/Label.java
old mode 100755
new mode 100644
index f37786b623..ccdf3111be
--- a/src/main/java/com/alibaba/fastjson/asm/Label.java
+++ b/src/main/java/com/alibaba/fastjson/asm/Label.java
@@ -34,7 +34,7 @@
* and for try catch blocks. A label designates the instruction that is just after. Note however that there can
* be other elements between a label and the instruction it designates (such as other labels, stack map frames, line
* numbers, etc.).
- *
+ *
* @author Eric Bruneton
*/
public class Label {
@@ -61,6 +61,31 @@ public class Label {
*/
private int[] srcAndRefPositions;
+ /**
+ * The bit mask to extract the type of a forward reference to this label. The extracted type is
+ * either {@link #FORWARD_REFERENCE_TYPE_SHORT} or {@link #FORWARD_REFERENCE_TYPE_WIDE}.
+ */
+ static final int FORWARD_REFERENCE_TYPE_MASK = 0xF0000000;
+
+ /**
+ * The bit mask to extract the 'handle' of a forward reference to this label. The extracted handle
+ * is the bytecode offset where the forward reference value is stored (using either 2 or 4 bytes,
+ * as indicated by the {@link #FORWARD_REFERENCE_TYPE_MASK}).
+ */
+ static final int FORWARD_REFERENCE_HANDLE_MASK = 0x0FFFFFFF;
+
+ /**
+ * The type of forward references stored with two bytes in the bytecode. This is the case, for
+ * instance, of a forward reference from an ifnull instruction.
+ */
+ static final int FORWARD_REFERENCE_TYPE_SHORT = 0x10000000;
+
+ /**
+ * The type of forward references stored in four bytes in the bytecode. This is the case, for
+ * instance, of a forward reference from a lookupswitch instruction.
+ */
+ static final int FORWARD_REFERENCE_TYPE_WIDE = 0x20000000;
+
// ------------------------------------------------------------------------
/*
@@ -109,7 +134,7 @@ public class Label {
* The next basic block in the basic block stack. This stack is used in the main loop of the fix point algorithm
* used in the second step of the control flow analysis algorithms. It is also used in {@link #visitSubroutine} to
* avoid using a recursive method.
- *
+ *
* @see MethodWriter#visitMaxs
*/
Label next;
@@ -132,7 +157,7 @@ public Label(){
* Puts a reference to this label in the bytecode of a method. If the position of the label is known, the offset is
* computed and written directly. Otherwise, a null offset is written and a new forward reference is declared for
* this label.
- *
+ *
* @param owner the code writer that calls this method.
* @param out the bytecode of the method.
* @param source the position of first byte of the bytecode instruction that contains this label.
@@ -140,12 +165,21 @@ public Label(){
* stored with 2 bytes.
* @throws IllegalArgumentException if this label has not been created by the given code writer.
*/
- void put(final MethodWriter owner, final ByteVector out, final int source) {
- if ((status & 2 /* RESOLVED */ ) == 0) {
- addReference(source, out.length);
- out.putShort(-1);
+ void put(final MethodWriter owner, final ByteVector out, final int source, boolean wideOffset) {
+ if ((status & 2 /* RESOLVED */) == 0) {
+ if (wideOffset) {
+ addReference(source, out.length, FORWARD_REFERENCE_TYPE_WIDE);
+ out.putInt(-1);
+ } else {
+ addReference(source, out.length, FORWARD_REFERENCE_TYPE_SHORT);
+ out.putShort(-1);
+ }
} else {
- out.putShort(position - source);
+ if (wideOffset) {
+ out.putInt(position - source);
+ } else {
+ out.putShort(position - source);
+ }
}
}
@@ -153,12 +187,12 @@ void put(final MethodWriter owner, final ByteVector out, final int source) {
* Adds a forward reference to this label. This method must be called only for a true forward reference, i.e. only
* if this label is not resolved yet. For backward references, the offset of the reference can be, and must be,
* computed and stored directly.
- *
+ *
* @param sourcePosition the position of the referencing instruction. This position will be used to compute the
* offset of this forward reference.
* @param referencePosition the position where the offset for this forward reference must be stored.
*/
- private void addReference(final int sourcePosition, final int referencePosition) {
+ private void addReference(final int sourcePosition, final int referencePosition, final int referenceType) {
if (srcAndRefPositions == null) {
srcAndRefPositions = new int[6];
}
@@ -168,14 +202,14 @@ private void addReference(final int sourcePosition, final int referencePosition)
srcAndRefPositions = a;
}
srcAndRefPositions[referenceCount++] = sourcePosition;
- srcAndRefPositions[referenceCount++] = referencePosition;
+ srcAndRefPositions[referenceCount++] = referencePosition | referenceType;
}
/**
* Resolves all forward references to this label. This method must be called when this label is added to the
* bytecode of the method, i.e. when its position becomes known. This method fills in the blanks that where left in
* the bytecode by each forward reference previously added to this label.
- *
+ *
* @param owner the code writer that calls this method.
* @param position the position of this label in the bytecode.
* @param data the bytecode of the method.
@@ -193,10 +227,17 @@ void resolve(final MethodWriter owner, final int position, final byte[] data) {
while (i < referenceCount) {
int source = srcAndRefPositions[i++];
int reference = srcAndRefPositions[i++];
+ int handle = reference & FORWARD_REFERENCE_HANDLE_MASK;
int offset = position - source;
- data[reference++] = (byte) (offset >>> 8);
- data[reference] = (byte) offset;
-
+ if ((reference & FORWARD_REFERENCE_TYPE_MASK) == FORWARD_REFERENCE_TYPE_SHORT) {
+ data[handle++] = (byte) (offset >>> 8);
+ data[handle] = (byte) offset;
+ } else {
+ data[handle++] = (byte) (offset >>> 24);
+ data[handle++] = (byte) (offset >>> 16);
+ data[handle++] = (byte) (offset >>> 8);
+ data[handle] = (byte) offset;
+ }
}
}
diff --git a/src/main/java/com/alibaba/fastjson/asm/MethodCollector.java b/src/main/java/com/alibaba/fastjson/asm/MethodCollector.java
index 4af294edf0..9f04b8f46b 100644
--- a/src/main/java/com/alibaba/fastjson/asm/MethodCollector.java
+++ b/src/main/java/com/alibaba/fastjson/asm/MethodCollector.java
@@ -11,14 +11,14 @@ public class MethodCollector {
private int currentParameter;
- private final StringBuffer result;
+ private final StringBuilder result;
protected boolean debugInfoPresent;
protected MethodCollector(int ignoreCount, int paramCount) {
this.ignoreCount = ignoreCount;
this.paramCount = paramCount;
- this.result = new StringBuffer();
+ this.result = new StringBuilder();
this.currentParameter = 0;
// if there are 0 parameters, there is no need for debug info
this.debugInfoPresent = paramCount == 0;
diff --git a/src/main/java/com/alibaba/fastjson/asm/MethodVisitor.java b/src/main/java/com/alibaba/fastjson/asm/MethodVisitor.java
old mode 100755
new mode 100644
diff --git a/src/main/java/com/alibaba/fastjson/asm/MethodWriter.java b/src/main/java/com/alibaba/fastjson/asm/MethodWriter.java
old mode 100755
new mode 100644
index 1cd633d2e0..743f63479e
--- a/src/main/java/com/alibaba/fastjson/asm/MethodWriter.java
+++ b/src/main/java/com/alibaba/fastjson/asm/MethodWriter.java
@@ -204,7 +204,8 @@ public void visitJumpInsn(final int opcode, final Label label) {
* needed).
*/
code.putByte(opcode);
- label.put(this, code, code.length - 1);
+ // Currently, GOTO_W is the only supported wide reference
+ label.put(this, code, code.length - 1, opcode == Opcodes.GOTO_W);
}
}
diff --git a/src/main/java/com/alibaba/fastjson/asm/Opcodes.java b/src/main/java/com/alibaba/fastjson/asm/Opcodes.java
old mode 100755
new mode 100644
index 3440da9804..4433068900
--- a/src/main/java/com/alibaba/fastjson/asm/Opcodes.java
+++ b/src/main/java/com/alibaba/fastjson/asm/Opcodes.java
@@ -134,6 +134,6 @@ public interface Opcodes {
int IFNULL = 198; // visitJumpInsn
int IFNONNULL = 199; // -
- // int GOTO_W = 200; // -
+ int GOTO_W = 200; // visitJumpInsn
// int JSR_W = 201; // -
}
diff --git a/src/main/java/com/alibaba/fastjson/asm/Type.java b/src/main/java/com/alibaba/fastjson/asm/Type.java
old mode 100755
new mode 100644
index 4a5da99996..8c9c804450
--- a/src/main/java/com/alibaba/fastjson/asm/Type.java
+++ b/src/main/java/com/alibaba/fastjson/asm/Type.java
@@ -280,7 +280,7 @@ protected String getClassName() {
return "double";
case 9: //ARRAY:
Type elementType = getType(buf, off + getDimensions());
- StringBuffer b = new StringBuffer(elementType.getClassName());
+ StringBuilder b = new StringBuilder(elementType.getClassName());
for (int i = getDimensions(); i > 0; --i) {
b.append("[]");
}
diff --git a/src/main/java/com/alibaba/fastjson/asm/TypeCollector.java b/src/main/java/com/alibaba/fastjson/asm/TypeCollector.java
index 32f82b94d4..16d38417e2 100644
--- a/src/main/java/com/alibaba/fastjson/asm/TypeCollector.java
+++ b/src/main/java/com/alibaba/fastjson/asm/TypeCollector.java
@@ -1,10 +1,14 @@
package com.alibaba.fastjson.asm;
+import com.alibaba.fastjson.util.ASMUtils;
+
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
public class TypeCollector {
+ private static String JSONType = ASMUtils.desc(com.alibaba.fastjson.annotation.JSONType.class);
+
private static final Map primitives = new HashMap() {
{
put("int","I");
@@ -24,6 +28,8 @@ public class TypeCollector {
protected MethodCollector collector;
+ protected boolean jsonType;
+
public TypeCollector(String methodName, Class>[] parameterTypes) {
this.methodName = methodName;
this.parameterTypes = parameterTypes;
@@ -62,19 +68,25 @@ protected MethodCollector visitMethod(int access, String name, String desc) {
argTypes.length + longOrDoubleQuantity);
}
+ public void visitAnnotation(String desc) {
+ if (JSONType.equals(desc)) {
+ jsonType = true;
+ }
+ }
+
private boolean correctTypeName(Type type, String paramTypeName) {
String s = type.getClassName();
// array notation needs cleanup.
- String braces = "";
+ StringBuilder braces = new StringBuilder();
while (s.endsWith("[]")) {
- braces = braces + "[";
+ braces.append('[');
s = s.substring(0, s.length() - 2);
}
- if (!braces.equals("")) {
+ if (braces.length() != 0) {
if (primitives.containsKey(s)) {
- s = braces + primitives.get(s);
+ s = braces.append(primitives.get(s)).toString();
} else {
- s = braces + "L" + s + ";";
+ s = braces.append('L').append(s).append(';').toString();
}
}
return s.equals(paramTypeName);
@@ -86,4 +98,12 @@ public String[] getParameterNamesForMethod() {
}
return collector.getResult().split(",");
}
+
+ public boolean matched() {
+ return collector != null;
+ }
+
+ public boolean hasJsonType() {
+ return jsonType;
+ }
}
\ No newline at end of file
diff --git a/src/main/java/com/alibaba/fastjson/parser/DefaultJSONParser.java b/src/main/java/com/alibaba/fastjson/parser/DefaultJSONParser.java
old mode 100755
new mode 100644
index 164487f5dc..210a952dea
--- a/src/main/java/com/alibaba/fastjson/parser/DefaultJSONParser.java
+++ b/src/main/java/com/alibaba/fastjson/parser/DefaultJSONParser.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2017 Alibaba Group.
+ * Copyright 1999-2019 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,8 +15,10 @@
*/
package com.alibaba.fastjson.parser;
-import static com.alibaba.fastjson.parser.JSONLexer.EOI;
-import static com.alibaba.fastjson.parser.JSONToken.*;
+import com.alibaba.fastjson.*;
+import com.alibaba.fastjson.parser.deserializer.*;
+import com.alibaba.fastjson.serializer.*;
+import com.alibaba.fastjson.util.TypeUtils;
import java.io.Closeable;
import java.lang.reflect.ParameterizedType;
@@ -29,10 +31,8 @@
import java.text.SimpleDateFormat;
import java.util.*;
-import com.alibaba.fastjson.*;
-import com.alibaba.fastjson.parser.deserializer.*;
-import com.alibaba.fastjson.serializer.*;
-import com.alibaba.fastjson.util.TypeUtils;
+import static com.alibaba.fastjson.parser.JSONLexer.EOI;
+import static com.alibaba.fastjson.parser.JSONToken.*;
/**
* @author wenshao[szujobs@hotmail.com]
@@ -67,6 +67,8 @@ public class DefaultJSONParser implements Closeable {
private List extraProcessors = null;
protected FieldTypeResolver fieldTypeResolver = null;
+ private int objectKeyLevel = 0;
+
private boolean autoTypeEnable;
private String[] autoTypeAccept = null;
@@ -95,9 +97,7 @@ public class DefaultJSONParser implements Closeable {
String.class
};
- for (Class> clazz : classes) {
- primitiveClasses.add(clazz);
- }
+ primitiveClasses.addAll(Arrays.asList(classes));
}
public String getDateFomartPattern() {
@@ -117,7 +117,15 @@ public void setDateFormat(String dateFormat) {
this.dateFormat = null;
}
+ /**
+ * @deprecated
+ * @see setDateFormat
+ */
public void setDateFomrat(DateFormat dateFormat) {
+ this.setDateFormat(dateFormat);
+ }
+
+ public void setDateFormat(DateFormat dateFormat) {
this.dateFormat = dateFormat;
}
@@ -177,24 +185,30 @@ public String getInput() {
@SuppressWarnings({ "unchecked", "rawtypes" })
public final Object parseObject(final Map object, Object fieldName) {
final JSONLexer lexer = this.lexer;
-
+
if (lexer.token() == JSONToken.NULL) {
lexer.nextToken();
return null;
}
-
+
if (lexer.token() == JSONToken.RBRACE) {
lexer.nextToken();
return object;
}
+ if (lexer.token() == JSONToken.LITERAL_STRING && lexer.stringVal().length() == 0) {
+ lexer.nextToken();
+ return object;
+ }
+
if (lexer.token() != JSONToken.LBRACE && lexer.token() != JSONToken.COMMA) {
throw new JSONException("syntax error, expect {, actual " + lexer.tokenName() + ", " + lexer.info());
}
ParseContext context = this.context;
try {
- Map map = object instanceof JSONObject ? ((JSONObject) object).getInnerMap() : object;
+ boolean isJsonObjectMap = object instanceof JSONObject;
+ Map map = isJsonObjectMap ? ((JSONObject) object).getInnerMap() : object;
boolean setContextFlag = false;
for (;;) {
@@ -254,11 +268,14 @@ public final Object parseObject(final Map object, Object fieldName) {
lexer.resetStringPosition();
lexer.scanNumber();
try {
- if (lexer.token() == JSONToken.LITERAL_INT) {
- key = lexer.integerValue();
- } else {
- key = lexer.decimalValue(true);
- }
+ if (lexer.token() == JSONToken.LITERAL_INT) {
+ key = lexer.integerValue();
+ } else {
+ key = lexer.decimalValue(true);
+ }
+ if (lexer.isEnabled(Feature.NonStringKeyAsString) || isJsonObjectMap) {
+ key = key.toString();
+ }
} catch (NumberFormatException e) {
throw new JSONException("parse number key error" + lexer.info());
}
@@ -267,6 +284,9 @@ public final Object parseObject(final Map object, Object fieldName) {
throw new JSONException("parse number key error" + lexer.info());
}
} else if (ch == '{' || ch == '[') {
+ if (objectKeyLevel++ > 512) {
+ throw new JSONException("object key level > 512");
+ }
lexer.nextToken();
key = parse();
isObjectKey = true;
@@ -304,8 +324,24 @@ public final Object parseObject(final Map object, Object fieldName) {
if (object != null
&& object.getClass().getName().equals(typeName)) {
clazz = object.getClass();
+ } else if ("java.util.HashMap".equals(typeName)) {
+ clazz = java.util.HashMap.class;
+ } else if ("java.util.LinkedHashMap".equals(typeName)) {
+ clazz = java.util.LinkedHashMap.class;
} else {
- clazz = config.checkAutoType(typeName, null, lexer.getFeatures());
+
+ boolean allDigits = true;
+ for (int i = 0; i < typeName.length(); ++i) {
+ char c = typeName.charAt(i);
+ if (c < '0' || c > '9') {
+ allDigits = false;
+ break;
+ }
+ }
+
+ if (!allDigits) {
+ clazz = config.checkAutoType(typeName, null, lexer.getFeatures());
+ }
}
if (clazz == null) {
@@ -320,19 +356,7 @@ public final Object parseObject(final Map object, Object fieldName) {
Object instance = null;
ObjectDeserializer deserializer = this.config.getDeserializer(clazz);
if (deserializer instanceof JavaBeanDeserializer) {
- JavaBeanDeserializer javaBeanDeserializer = (JavaBeanDeserializer) deserializer;
- instance = javaBeanDeserializer.createInstance(this, clazz);
-
- for (Object o : map.entrySet()) {
- Map.Entry entry = (Map.Entry) o;
- Object entryKey = entry.getKey();
- if (entryKey instanceof String) {
- FieldDeserializer fieldDeserializer = javaBeanDeserializer.getFieldDeserializer((String) entryKey);
- if (fieldDeserializer != null) {
- fieldDeserializer.setValue(instance, entry.getValue());
- }
- }
- }
+ instance = TypeUtils.cast(object, clazz, this.config);
}
if (instance == null) {
@@ -340,6 +364,8 @@ public final Object parseObject(final Map object, Object fieldName) {
instance = new HashMap();
} else if ("java.util.Collections$EmptyMap".equals(typeName)) {
instance = Collections.emptyMap();
+ } else if ("java.util.Collections$UnmodifiableMap".equals(typeName)) {
+ instance = Collections.unmodifiableMap(new HashMap());
} else {
instance = clazz.newInstance();
}
@@ -350,7 +376,7 @@ public final Object parseObject(final Map object, Object fieldName) {
throw new JSONException("create instance error", e);
}
}
-
+
this.setResolveStatus(TypeNameRedirect);
if (this.context != null
@@ -359,25 +385,41 @@ public final Object parseObject(final Map object, Object fieldName) {
&& !(this.context.fieldName instanceof Integer)) {
this.popContext();
}
-
+
if (object.size() > 0) {
Object newObj = TypeUtils.cast(object, clazz, this.config);
+ this.setResolveStatus(NONE);
this.parseObject(newObj);
return newObj;
}
ObjectDeserializer deserializer = config.getDeserializer(clazz);
- return deserializer.deserialze(this, clazz, fieldName);
+ Class deserClass = deserializer.getClass();
+ if (JavaBeanDeserializer.class.isAssignableFrom(deserClass)
+ && deserClass != JavaBeanDeserializer.class
+ && deserClass != ThrowableDeserializer.class) {
+ this.setResolveStatus(NONE);
+ } else if (deserializer instanceof MapDeserializer) {
+ this.setResolveStatus(NONE);
+ }
+ Object obj = deserializer.deserialze(this, clazz, fieldName);
+ return obj;
}
if (key == "$ref"
&& context != null
+ && (object == null || object.size() == 0)
&& !lexer.isEnabled(Feature.DisableSpecialKeyDetect)) {
lexer.nextToken(JSONToken.LITERAL_STRING);
if (lexer.token() == JSONToken.LITERAL_STRING) {
String ref = lexer.stringVal();
lexer.nextToken(JSONToken.RBRACE);
+ if (lexer.token() == JSONToken.COMMA) {
+ map.put(key, ref);
+ continue;
+ }
+
Object refValue = null;
if ("@".equals(ref)) {
if (this.context != null) {
@@ -409,12 +451,18 @@ public final Object parseObject(final Map object, Object fieldName) {
setResolveStatus(DefaultJSONParser.NeedToResolve);
}
} else {
- addResolveTask(new ResolveTask(context, ref));
- setResolveStatus(DefaultJSONParser.NeedToResolve);
+ JSONPath jsonpath = JSONPath.compile(ref);
+ if (jsonpath.isRef()) {
+ addResolveTask(new ResolveTask(context, ref));
+ setResolveStatus(DefaultJSONParser.NeedToResolve);
+ } else {
+ refValue = new JSONObject()
+ .fluentPut("$ref", ref);
+ }
}
if (lexer.token() != JSONToken.RBRACE) {
- throw new JSONException("syntax error");
+ throw new JSONException("syntax error, " + lexer.info());
}
lexer.nextToken(JSONToken.COMMA);
@@ -480,7 +528,7 @@ public final Object parseObject(final Map object, Object fieldName) {
}
this.parseArray(list, key);
-
+
if (lexer.isEnabled(Feature.UseObjectArray)) {
value = list.toArray();
} else {
@@ -496,16 +544,26 @@ public final Object parseObject(final Map object, Object fieldName) {
} else {
throw new JSONException("syntax error");
}
- } else if (ch == '{') { // 减少嵌套,兼容android
+ } else if (ch == '{') { // 减少嵌套,兼容 Android
lexer.nextToken();
final boolean parentIsArray = fieldName != null && fieldName.getClass() == Integer.class;
- JSONObject input = new JSONObject(lexer.isEnabled(Feature.OrderedField));
+ Map input;
+ if (lexer.isEnabled(Feature.CustomMapDeserializer)) {
+ MapDeserializer mapDeserializer = (MapDeserializer) config.getDeserializer(Map.class);
+
+
+ input = (lexer.getFeatures() & Feature.OrderedField.mask) != 0
+ ? mapDeserializer.createMap(Map.class, lexer.getFeatures())
+ : mapDeserializer.createMap(Map.class);
+ } else {
+ input = new JSONObject(lexer.isEnabled(Feature.OrderedField));
+ }
ParseContext ctxLocal = null;
if (!parentIsArray) {
- ctxLocal = setContext(context, input, key);
+ ctxLocal = setContext(this.context, input, key);
}
Object obj = null;
@@ -522,7 +580,7 @@ public final Object parseObject(final Map object, Object fieldName) {
if (!objParsed) {
obj = this.parseObject(input, key);
}
-
+
if (ctxLocal != null && input != obj) {
ctxLocal.object = object;
}
@@ -530,7 +588,7 @@ public final Object parseObject(final Map object, Object fieldName) {
if (key != null) {
checkMapResolve(object, key.toString());
}
-
+
map.put(key, obj);
if (parentIsArray) {
@@ -607,7 +665,7 @@ public void setConfig(ParserConfig config) {
public T parseObject(Class clazz) {
return (T) parseObject(clazz, null);
}
-
+
public T parseObject(Type type) {
return parseObject(type, null);
}
@@ -617,7 +675,8 @@ public T parseObject(Type type, Object fieldName) {
int token = lexer.token();
if (token == JSONToken.NULL) {
lexer.nextToken();
- return null;
+
+ return (T) TypeUtils.optionalEmpty(type);
}
if (token == JSONToken.LITERAL_STRING) {
@@ -634,10 +693,17 @@ public T parseObject(Type type, Object fieldName) {
}
}
- ObjectDeserializer derializer = config.getDeserializer(type);
+ ObjectDeserializer deserializer = config.getDeserializer(type);
try {
- return (T) derializer.deserialze(this, type, fieldName);
+ if (deserializer.getClass() == JavaBeanDeserializer.class) {
+ if (lexer.token()!= JSONToken.LBRACE && lexer.token()!=JSONToken.LBRACKET) {
+ throw new JSONException("syntax error,expect start with { or [,but actually start with "+ lexer.tokenName());
+ }
+ return (T) ((JavaBeanDeserializer) deserializer).deserialze(this, type, fieldName, 0);
+ } else {
+ return (T) deserializer.deserialze(this, type, fieldName);
+ }
} catch (JSONException e) {
throw e;
} catch (Throwable e) {
@@ -669,7 +735,7 @@ public void parseArray(Type type, Collection array, Object fieldName) {
}
if (token != JSONToken.LBRACKET) {
- throw new JSONException("exepct '[', but " + JSONToken.name(token) + ", " + lexer.info());
+ throw new JSONException("field " + fieldName + " expect '[', but " + JSONToken.name(token) + ", " + lexer.info());
}
ObjectDeserializer deserializer = null;
@@ -795,8 +861,12 @@ public Object[] parseArray(Type[] types) {
if (i == types.length - 1) {
if (type instanceof Class) {
Class> clazz = (Class>) type;
- isArray = clazz.isArray();
- componentType = clazz.getComponentType();
+ //如果最后一个type是字节数组,且当前token为字符串类型,不应该当作可变长参数进行处理
+ //而是作为一个整体的Base64字符串进行反序列化
+ if (!((clazz == byte[].class || clazz == char[].class) && lexer.token() == LITERAL_STRING)) {
+ isArray = clazz.isArray();
+ componentType = clazz.getComponentType();
+ }
}
}
@@ -804,12 +874,12 @@ public Object[] parseArray(Type[] types) {
if (isArray && lexer.token() != JSONToken.LBRACKET) {
List varList = new ArrayList();
- ObjectDeserializer derializer = config.getDeserializer(componentType);
- int fastMatch = derializer.getFastMatchToken();
+ ObjectDeserializer deserializer = config.getDeserializer(componentType);
+ int fastMatch = deserializer.getFastMatchToken();
if (lexer.token() != JSONToken.RBRACKET) {
for (;;) {
- Object item = derializer.deserialze(this, type, null);
+ Object item = deserializer.deserialze(this, type, null);
varList.add(item);
if (lexer.token() == JSONToken.COMMA) {
@@ -824,8 +894,8 @@ public Object[] parseArray(Type[] types) {
value = TypeUtils.cast(varList, type, config);
} else {
- ObjectDeserializer derializer = config.getDeserializer(type);
- value = derializer.deserialze(this, type, i);
+ ObjectDeserializer deserializer = config.getDeserializer(type);
+ value = deserializer.deserialze(this, type, i);
}
}
}
@@ -858,9 +928,9 @@ public Object[] parseArray(Type[] types) {
public void parseObject(Object object) {
Class> clazz = object.getClass();
JavaBeanDeserializer beanDeser = null;
- ObjectDeserializer deserizer = config.getDeserializer(clazz);
- if (deserizer instanceof JavaBeanDeserializer) {
- beanDeser = (JavaBeanDeserializer) deserizer;
+ ObjectDeserializer deserializer = config.getDeserializer(clazz);
+ if (deserializer instanceof JavaBeanDeserializer) {
+ beanDeser = (JavaBeanDeserializer) deserializer;
}
if (lexer.token() != JSONToken.LBRACE && lexer.token() != JSONToken.COMMA) {
@@ -887,7 +957,7 @@ public void parseObject(Object object) {
if (beanDeser != null) {
fieldDeser = beanDeser.getFieldDeserializer(key);
}
-
+
if (fieldDeser == null) {
if (!lexer.isEnabled(Feature.IgnoreNotMatch)) {
throw new JSONException("setter not found, class " + clazz.getName() + ", property " + key);
@@ -1081,8 +1151,17 @@ public Object parseObject(final Map object) {
public JSONObject parseObject() {
JSONObject object = new JSONObject(lexer.isEnabled(Feature.OrderedField));
- object = (JSONObject) parseObject(object);
- return object;
+ Object parsedObject = parseObject(object);
+
+ if (parsedObject instanceof JSONObject) {
+ return (JSONObject) parsedObject;
+ }
+
+ if (parsedObject == null) {
+ return null;
+ }
+
+ return new JSONObject((Map) parsedObject);
}
@SuppressWarnings("rawtypes")
@@ -1105,10 +1184,14 @@ public final void parseArray(final Collection array, Object fieldName) {
lexer.nextToken(JSONToken.LITERAL_STRING);
+ if (this.context != null && this.context.level > 512) {
+ throw new JSONException("array level > 512");
+ }
+
ParseContext context = this.context;
this.setContext(array, fieldName);
try {
- for (int i = 0;; ++i) {
+ for (int i = 0; ; ++i) {
if (lexer.isEnabled(Feature.AllowArbitraryCommas)) {
while (lexer.token() == JSONToken.COMMA) {
lexer.nextToken();
@@ -1194,6 +1277,8 @@ public final void parseArray(final Collection array, Object fieldName) {
continue;
}
}
+ } catch (ClassCastException e) {
+ throw new JSONException("unkown error", e);
} finally {
this.setContext(context);
}
@@ -1203,6 +1288,10 @@ public ParseContext getContext() {
return context;
}
+ public ParseContext getOwnerContext() {
+ return context.parent;
+ }
+
public List getResolveTaskList() {
if (resolveTaskList == null) {
resolveTaskList = new ArrayList(2);
@@ -1238,7 +1327,7 @@ public List getExtraTypeProviders() {
public FieldTypeResolver getFieldTypeResolver() {
return fieldTypeResolver;
}
-
+
public void setFieldTypeResolver(FieldTypeResolver fieldTypeResolver) {
this.fieldTypeResolver = fieldTypeResolver;
}
@@ -1324,14 +1413,20 @@ public Object parse(Object fieldName) {
parseArray(treeSet, fieldName);
return treeSet;
case LBRACKET:
- JSONArray array = new JSONArray();
+ Collection array = isEnabled(Feature.UseNativeJavaObject)
+ ? new ArrayList()
+ : new JSONArray();
parseArray(array, fieldName);
if (lexer.isEnabled(Feature.UseObjectArray)) {
return array.toArray();
}
return array;
case LBRACE:
- JSONObject object = new JSONObject(lexer.isEnabled(Feature.OrderedField));
+ Map object = isEnabled(Feature.UseNativeJavaObject)
+ ? lexer.isEnabled(Feature.OrderedField)
+ ? new HashMap()
+ : new LinkedHashMap()
+ : new JSONObject(lexer.isEnabled(Feature.OrderedField));
return parseObject(object, fieldName);
// case LBRACE: {
// Map map = lexer.isEnabled(Feature.OrderedField)
@@ -1388,7 +1483,7 @@ public Object parse(Object fieldName) {
lexer.nextToken(JSONToken.LPAREN);
accept(JSONToken.LPAREN);
- long time = ((Number) lexer.integerValue()).longValue();
+ long time = lexer.integerValue().longValue();
accept(JSONToken.LITERAL_INT);
accept(JSONToken.RPAREN);
@@ -1446,12 +1541,12 @@ public final void accept(final int token, int nextExpectToken) {
throwException(token);
}
}
-
+
public void throwException(int token) {
throw new JSONException("syntax error, expect " + JSONToken.name(token) + ", actual "
+ JSONToken.name(lexer.token()));
}
-
+
public void close() {
final JSONLexer lexer = this.lexer;
@@ -1499,7 +1594,10 @@ public void handleResovleTask(Object value) {
refValue = getObject(ref);
if (refValue == null) {
try {
- refValue = JSONPath.eval(value, ref);
+ JSONPath jsonpath = new JSONPath(ref, SerializeConfig.getGlobalInstance(), config, true);
+ if (jsonpath.isRef()) {
+ refValue = jsonpath.eval(value);
+ }
} catch (JSONPathException ex) {
// skip
}
@@ -1516,7 +1614,23 @@ public void handleResovleTask(Object value) {
&& fieldDeser.fieldInfo != null
&& !Map.class.isAssignableFrom(fieldDeser.fieldInfo.fieldClass)) {
Object root = this.contextArray[0].object;
- refValue = JSONPath.eval(root, ref);
+ JSONPath jsonpath = JSONPath.compile(ref);
+ if (jsonpath.isRef()) {
+ refValue = jsonpath.eval(root);
+ }
+ }
+
+ // workaround for bug
+ if (fieldDeser.getOwnerClass() != null
+ && (!fieldDeser.getOwnerClass().isInstance(object))
+ && task.ownerContext.parent != null
+ ) {
+ for (ParseContext ctx = task.ownerContext.parent;ctx != null;ctx = ctx.parent) {
+ if (fieldDeser.getOwnerClass().isInstance(ctx.object)) {
+ object = ctx.object;
+ break;
+ }
+ }
}
fieldDeser.setValue(object, refValue);
@@ -1536,12 +1650,12 @@ public ResolveTask(ParseContext context, String referenceValue){
this.referenceValue = referenceValue;
}
}
-
+
public void parseExtra(Object object, String key) {
final JSONLexer lexer = this.lexer; // xxx
lexer.nextTokenWithColon();
Type type = null;
-
+
if (extraTypeProviders != null) {
for (ExtraTypeProvider extraProvider : extraTypeProviders) {
type = extraProvider.getExtraType(object, key);
@@ -1550,7 +1664,7 @@ public void parseExtra(Object object, String key) {
Object value = type == null //
? parse() // skip
: parseObject(type);
-
+
if (object instanceof ExtraProcessable) {
ExtraProcessable extraProcessable = ((ExtraProcessable) object);
extraProcessable.processExtra(key, value);
@@ -1709,4 +1823,5 @@ public Object parse(PropertyProcessable object, Object fieldName) {
setContext(context);
}
}
+
}
diff --git a/src/main/java/com/alibaba/fastjson/parser/Feature.java b/src/main/java/com/alibaba/fastjson/parser/Feature.java
old mode 100755
new mode 100644
index ffea390d40..fc971d6c35
--- a/src/main/java/com/alibaba/fastjson/parser/Feature.java
+++ b/src/main/java/com/alibaba/fastjson/parser/Feature.java
@@ -125,7 +125,38 @@ public enum Feature {
/**
* @since 1.2.41, backport to 1.1.66.android
*/
- SupportAutoType
+ SupportAutoType,
+
+ /**
+ * @since 1.2.42
+ */
+ NonStringKeyAsString,
+
+ /**
+ * @since 1.2.45
+ */
+ CustomMapDeserializer,
+
+ /**
+ * @since 1.2.55
+ */
+ ErrorOnEnumNotMatch,
+
+ /**
+ * @since 1.2.68
+ */
+ SafeMode,
+
+ /**
+ * @since 1.2.72
+ */
+ TrimStringFieldValue,
+
+ /**
+ * @since 1.2.77
+ * use HashMap instead of JSONObject, ArrayList instead of JSONArray
+ */
+ UseNativeJavaObject
;
Feature(){
diff --git a/src/main/java/com/alibaba/fastjson/parser/JSONLexer.java b/src/main/java/com/alibaba/fastjson/parser/JSONLexer.java
index 248eac66ec..4cb954556f 100644
--- a/src/main/java/com/alibaba/fastjson/parser/JSONLexer.java
+++ b/src/main/java/com/alibaba/fastjson/parser/JSONLexer.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright 1999-2019 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
package com.alibaba.fastjson.parser;
import java.math.BigDecimal;
@@ -10,7 +25,7 @@ public interface JSONLexer {
char EOI = 0x1A;
int NOT_MATCH = -1;
int NOT_MATCH_NAME = -2;
- int UNKNOWN = 0;
+ int UNKNOWN = 0;
int OBJECT = 1;
int ARRAY = 2;
int VALUE = 3;
@@ -52,7 +67,7 @@ public interface JSONLexer {
String stringVal();
boolean isEnabled(int feature);
-
+
boolean isEnabled(Feature feature);
void config(Feature feature, boolean state);
@@ -73,6 +88,8 @@ public interface JSONLexer {
boolean isRef();
+ String scanTypeName(SymbolTable symbolTable);
+
String numberString();
byte[] bytesValue();
@@ -95,14 +112,15 @@ public interface JSONLexer {
void scanStringArray(Collection collection, char seperator);
TimeZone getTimeZone();
-
+
void setTimeZone(TimeZone timeZone);
-
+
Locale getLocale();
-
+
void setLocale(Locale locale);
-
+
String info();
int getFeatures();
+ void setFeatures(int features);
}
diff --git a/src/main/java/com/alibaba/fastjson/parser/JSONLexerBase.java b/src/main/java/com/alibaba/fastjson/parser/JSONLexerBase.java
old mode 100755
new mode 100644
index 28efd2c3f2..ca70c87ffa
--- a/src/main/java/com/alibaba/fastjson/parser/JSONLexerBase.java
+++ b/src/main/java/com/alibaba/fastjson/parser/JSONLexerBase.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2017 Alibaba Group.
+ * Copyright 1999-2019 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,7 +16,6 @@
package com.alibaba.fastjson.parser;
import java.io.Closeable;
-import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
@@ -24,11 +23,11 @@
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
-import com.alibaba.fastjson.annotation.JSONType;
import com.alibaba.fastjson.util.IOUtils;
-import com.alibaba.fastjson.util.TypeUtils;
import static com.alibaba.fastjson.parser.JSONToken.*;
+import static com.alibaba.fastjson.util.TypeUtils.fnv1a_64_magic_hashcode;
+import static com.alibaba.fastjson.util.TypeUtils.fnv1a_64_magic_prime;
/**
* @author wenshao[szujobs@hotmail.com]
@@ -70,6 +69,7 @@ protected void lexError(String key, Object... args) {
private final static ThreadLocal SBUF_LOCAL = new ThreadLocal();
protected String stringDefaultValue = null;
+ protected int nanos = 0;
public JSONLexerBase(int features){
this.features = features;
@@ -209,7 +209,7 @@ public final void nextToken() {
}
token = EOF;
- pos = bp = eofPos;
+ eofPos = pos = bp;
} else {
if (ch <= 31 || ch == 127) {
next();
@@ -266,6 +266,11 @@ public final void nextToken(int expect) {
token = JSONToken.EOF;
return;
}
+
+ if (ch == 'n') {
+ scanNullOrNew(false);
+ return;
+ }
break;
case JSONToken.LITERAL_INT:
if (ch >= '0' && ch <= '9') {
@@ -456,11 +461,11 @@ public final Number integerValue() throws NumberFormatException {
// Accumulating negatively avoids surprises near MAX_VALUE
digit = charAt(i++) - '0';
if (result < multmin) {
- return new BigInteger(numberString());
+ return new BigInteger(numberString(), 10);
}
result *= 10;
if (result < limit + digit) {
- return new BigInteger(numberString());
+ return new BigInteger(numberString(), 10);
}
result -= digit;
}
@@ -480,7 +485,7 @@ public final Number integerValue() throws NumberFormatException {
}
return result;
} else { /* Only got "-" */
- throw new NumberFormatException(numberString());
+ throw new JSONException("illegal number format : " + numberString());
}
} else {
result = -result;
@@ -969,18 +974,27 @@ public final void scanString() {
putChar('\\');
break;
case 'x':
- char x1 = ch = next();
- char x2 = ch = next();
+ char x1 = next();
+ char x2 = next();
+
+ boolean hex1 = (x1 >= '0' && x1 <= '9')
+ || (x1 >= 'a' && x1 <= 'f')
+ || (x1 >= 'A' && x1 <= 'F');
+ boolean hex2 = (x2 >= '0' && x2 <= '9')
+ || (x2 >= 'a' && x2 <= 'f')
+ || (x2 >= 'A' && x2 <= 'F');
+ if (!hex1 || !hex2) {
+ throw new JSONException("invalid escape character \\x" + x1 + x2);
+ }
- int x_val = digits[x1] * 16 + digits[x2];
- char x_char = (char) x_val;
+ char x_char = (char) (digits[x1] * 16 + digits[x2]);
putChar(x_char);
break;
case 'u':
- char u1 = ch = next();
- char u2 = ch = next();
- char u3 = ch = next();
- char u4 = ch = next();
+ char u1 = next();
+ char u2 = next();
+ char u3 = next();
+ char u4 = next();
int val = Integer.parseInt(new String(new char[] { u1, u2, u3, u4 }), 16);
putChar((char) val);
break;
@@ -1101,6 +1115,10 @@ && charAt(np + 3) == 'e' //
&& charAt(np + 4) == 'f';
}
+ public String scanTypeName(SymbolTable symbolTable) {
+ return null;
+ }
+
protected final static char[] typeFieldName = ("\"" + JSON.DEFAULT_TYPE_KEY + "\":\"").toCharArray();
public final int scanType(String type) {
@@ -1186,6 +1204,38 @@ public final boolean matchField(char[] fieldName) {
return true;
}
+ public int matchField(long fieldNameHash) {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean seekArrayToItem(int index) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int seekObjectToField(long fieldNameHash, boolean deepScan) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int seekObjectToField(long[] fieldNameHash) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int seekObjectToFieldDeepScan(long fieldNameHash) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void skipObject() {
+ throw new UnsupportedOperationException();
+ }
+
+ public void skipObject(boolean valid) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void skipArray() {
+ throw new UnsupportedOperationException();
+ }
+
public abstract int indexOf(char ch, int startIndex);
public abstract String addSymbol(int offset, int len, int hash, final SymbolTable symbolTable);
@@ -1310,8 +1360,6 @@ public String scanString(char expectNextChar) {
}
}
-
-
final String strVal;
for (;;) {
if (chLocal == '"') {
@@ -1363,12 +1411,17 @@ public String scanString(char expectNextChar) {
bp += offset;
this.ch = charAt(bp);
matchStat = VALUE;
+ token = JSONToken.COMMA;
return strVal;
} else if (isWhitespace(chLocal)) {
chLocal = charAt(bp + (offset++));
continue;
} else {
- matchStat = NOT_MATCH;
+ if (chLocal == ']') {
+ bp += offset;
+ this.ch = charAt(bp);
+ matchStat = NOT_MATCH;
+ }
return strVal;
}
}
@@ -1390,7 +1443,7 @@ public long scanFieldSymbol(char[] fieldName) {
return 0;
}
- long hash = 0xcbf29ce484222325L;
+ long hash = fnv1a_64_magic_hashcode;
for (;;) {
chLocal = charAt(bp + (offset++));
if (chLocal == '\"') {
@@ -1399,7 +1452,78 @@ public long scanFieldSymbol(char[] fieldName) {
}
hash ^= chLocal;
- hash *= 0x100000001b3L;
+ hash *= fnv1a_64_magic_prime;
+
+ if (chLocal == '\\') {
+ matchStat = NOT_MATCH;
+ return 0;
+ }
+ }
+
+ if (chLocal == ',') {
+ bp += offset;
+ this.ch = this.charAt(bp);
+ matchStat = VALUE;
+ return hash;
+ }
+
+ if (chLocal == '}') {
+ chLocal = charAt(bp + (offset++));
+ if (chLocal == ',') {
+ token = JSONToken.COMMA;
+ bp += offset;
+ this.ch = this.charAt(bp);
+ } else if (chLocal == ']') {
+ token = JSONToken.RBRACKET;
+ bp += offset;
+ this.ch = this.charAt(bp);
+ } else if (chLocal == '}') {
+ token = JSONToken.RBRACE;
+ bp += offset;
+ this.ch = this.charAt(bp);
+ } else if (chLocal == EOI) {
+ token = JSONToken.EOF;
+ bp += (offset - 1);
+ ch = EOI;
+ } else {
+ matchStat = NOT_MATCH;
+ return 0;
+ }
+ matchStat = END;
+ } else {
+ matchStat = NOT_MATCH;
+ return 0;
+ }
+
+ return hash;
+ }
+
+ public long scanEnumSymbol(char[] fieldName) {
+ matchStat = UNKNOWN;
+
+ if (!charArrayCompare(fieldName)) {
+ matchStat = NOT_MATCH_NAME;
+ return 0;
+ }
+
+ int offset = fieldName.length;
+ char chLocal = charAt(bp + (offset++));
+
+ if (chLocal != '"') {
+ matchStat = NOT_MATCH;
+ return 0;
+ }
+
+ long hash = fnv1a_64_magic_hashcode;
+ for (;;) {
+ chLocal = charAt(bp + (offset++));
+ if (chLocal == '\"') {
+ chLocal = charAt(bp + (offset++));
+ break;
+ }
+
+ hash ^= ((chLocal >= 'A' && chLocal <= 'Z') ? (chLocal + 32) : chLocal);
+ hash *= fnv1a_64_magic_prime;
if (chLocal == '\\') {
matchStat = NOT_MATCH;
@@ -1528,15 +1652,14 @@ public String scanSymbolWithSeperator(final SymbolTable symbolTable, char serper
public Collection newCollectionByType(Class> type){
if (type.isAssignableFrom(HashSet.class)) {
- HashSet list = new HashSet();
- return list;
+ return new HashSet();
} else if (type.isAssignableFrom(ArrayList.class)) {
- ArrayList list2 = new ArrayList();
- return list2;
+ return new ArrayList();
+ } else if (type.isAssignableFrom(LinkedList.class)) {
+ return new LinkedList();
} else {
try {
- Collection list = (Collection) type.newInstance();
- return list;
+ return (Collection) type.newInstance();
} catch (Exception e) {
throw new JSONException(e.getMessage(), e);
}
@@ -1692,7 +1815,7 @@ && charAt(bp + offset) == 'u'
&& charAt(bp + offset + 1) == 'l'
&& charAt(bp + offset + 2) == 'l'
&& charAt(bp + offset + 3) == seperator
- ) {
+ ) {
bp += 5;
ch = charAt(bp);
matchStat = VALUE_NULL;
@@ -2408,7 +2531,7 @@ public final float scanFieldFloat(char[] fieldName) {
float value;
if (chLocal >= '0' && chLocal <= '9') {
- int intVal = chLocal - '0';
+ long intVal = chLocal - '0';
for (;;) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
@@ -2419,7 +2542,7 @@ public final float scanFieldFloat(char[] fieldName) {
}
}
- int power = 1;
+ long power = 1;
boolean small = (chLocal == '.');
if (small) {
chLocal = charAt(bp + (offset++));
@@ -2472,8 +2595,8 @@ public final float scanFieldFloat(char[] fieldName) {
count = bp + offset - start - 1;
}
- if (!exp && count < 20) {
- value = ((float) intVal) / power;
+ if ((!exp) && count < 17) {
+ value = (float) (((double) intVal) / power);
if (negative) {
value = -value;
}
@@ -2652,8 +2775,8 @@ public final float scanFloat(char seperator) {
count = bp + offset - start - 1;
}
- if (!exp && count < 20) {
- value = ((float) intVal) / power;
+ if ((!exp) && count < 17) {
+ value = (float) (((double) intVal) / power);
if (negative) {
value = -value;
}
@@ -2790,7 +2913,7 @@ public double scanDouble(char seperator) {
count = bp + offset - start - 1;
}
- if (!exp && count < 20) {
+ if (!exp && count < 17) {
value = ((double) intVal) / power;
if (negative) {
value = -value;
@@ -2921,8 +3044,11 @@ public BigDecimal scanDecimal(char seperator) {
count = bp + offset - start - 1;
}
+ if (count > 65535) {
+ throw new JSONException("decimal overflow");
+ }
char[] chars = this.sub_chars(start, count);
- value = new BigDecimal(chars);
+ value = new BigDecimal(chars, 0, chars.length, MathContext.UNLIMITED);
} else if (chLocal == 'n' && charAt(bp + offset) == 'u' && charAt(bp + offset + 1) == 'l' && charAt(bp + offset + 2) == 'l') {
matchStat = VALUE_NULL;
value = null;
@@ -3291,6 +3417,8 @@ public final float[][] scanFieldFloatArray2(char[] fieldName) {
chLocal = charAt(bp + (offset++));
break;
}
+ } else {
+ break;
}
}
@@ -3427,7 +3555,7 @@ public final double scanFieldDouble(char[] fieldName) {
count = bp + offset - start - 1;
}
- if (!exp && count < 20) {
+ if (!exp && count < 17) {
value = ((double) intVal) / power;
if (negative) {
value = -value;
@@ -3436,7 +3564,10 @@ public final double scanFieldDouble(char[] fieldName) {
String text = this.subString(start, count);
value = Double.parseDouble(text);
}
- } else if (chLocal == 'n' && charAt(bp + offset) == 'u' && charAt(bp + offset + 1) == 'l' && charAt(bp + offset + 2) == 'l') {
+ } else if (chLocal == 'n' &&
+ charAt(bp + offset) == 'u' &&
+ charAt(bp + offset + 1) == 'l' &&
+ charAt(bp + offset + 2) == 'l') {
matchStat = VALUE_NULL;
value = 0;
offset += 3;
@@ -3590,9 +3721,16 @@ public BigDecimal scanFieldDecimal(char[] fieldName) {
count = bp + offset - start - 1;
}
+ if (count > 65535) {
+ throw new JSONException("scan decimal overflow");
+ }
+
char[] chars = this.sub_chars(start, count);
- value = new BigDecimal(chars);
- } else if (chLocal == 'n' && charAt(bp + offset) == 'u' && charAt(bp + offset + 1) == 'l' && charAt(bp + offset + 2) == 'l') {
+ value = new BigDecimal(chars, 0, chars.length, MathContext.UNLIMITED);
+ } else if (chLocal == 'n' &&
+ charAt(bp + offset) == 'u' &&
+ charAt(bp + offset + 1) == 'l' &&
+ charAt(bp + offset + 2) == 'l') {
matchStat = VALUE_NULL;
value = null;
offset += 3;
@@ -3690,10 +3828,17 @@ public BigInteger scanFieldBigInteger(char[] fieldName) {
BigInteger value;
if (chLocal >= '0' && chLocal <= '9') {
long intVal = chLocal - '0';
+ boolean overflow = false;
+ long temp;
for (;;) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
- intVal = intVal * 10 + (chLocal - '0');
+ temp = intVal * 10 + (chLocal - '0');
+ if (temp < intVal) {
+ overflow = true;
+ break;
+ }
+ intVal = temp;
continue;
} else {
break;
@@ -3715,16 +3860,23 @@ public BigInteger scanFieldBigInteger(char[] fieldName) {
count = bp + offset - start - 1;
}
- if (count < 20 || (negative && count < 21)) {
+ if (!overflow && (count < 20 || (negative && count < 21))) {
value = BigInteger.valueOf(negative ? -intVal : intVal);
} else {
// char[] chars = this.sub_chars(negative ? start + 1 : start, count);
// value = new BigInteger(chars, )
+ if (count > 65535) {
+ throw new JSONException("scanInteger overflow");
+ }
+
String strVal = this.subString(start, count);
- value = new BigInteger(strVal);
+ value = new BigInteger(strVal, 10);
}
- } else if (chLocal == 'n' && charAt(bp + offset) == 'u' && charAt(bp + offset + 1) == 'l' && charAt(bp + offset + 2) == 'l') {
+ } else if (chLocal == 'n' &&
+ charAt(bp + offset) == 'u' &&
+ charAt(bp + offset + 1) == 'l' &&
+ charAt(bp + offset + 2) == 'l') {
matchStat = VALUE_NULL;
value = null;
offset += 3;
@@ -4017,7 +4169,10 @@ public java.util.Date scanDate(char seperator) {
}
dateVal = new java.util.Date(millis);
- } else if (chLocal == 'n' && charAt(bp + offset) == 'u' && charAt(bp + offset + 1) == 'l' && charAt(bp + offset + 2) == 'l') {
+ } else if (chLocal == 'n' &&
+ charAt(bp + offset) == 'u' &&
+ charAt(bp + offset + 1) == 'l' &&
+ charAt(bp + offset + 2) == 'l') {
matchStat = VALUE_NULL;
dateVal = null;
offset += 3;
@@ -4498,8 +4653,18 @@ public final void scanTrue() {
}
next();
- if (ch == ' ' || ch == ',' || ch == '}' || ch == ']' || ch == '\n' || ch == '\r' || ch == '\t' || ch == EOI
- || ch == '\f' || ch == '\b' || ch == ':' || ch == '/') {
+ if (ch == ' ' ||
+ ch == ',' ||
+ ch == '}' ||
+ ch == ']' ||
+ ch == '\n' ||
+ ch == '\r' ||
+ ch == '\t' ||
+ ch == EOI ||
+ ch == '\f' ||
+ ch == '\b' ||
+ ch == ':' ||
+ ch == '/') {
token = JSONToken.TRUE;
} else {
throw new JSONException("scan true error");
@@ -4507,6 +4672,10 @@ public final void scanTrue() {
}
public final void scanNullOrNew() {
+ scanNullOrNew(true);
+ }
+
+ public final void scanNullOrNew(boolean acceptColon) {
if (ch != 'n') {
throw new JSONException("error parse null or new");
}
@@ -4524,8 +4693,19 @@ public final void scanNullOrNew() {
}
next();
- if (ch == ' ' || ch == ',' || ch == '}' || ch == ']' || ch == '\n' || ch == '\r' || ch == '\t' || ch == EOI
- || ch == '\f' || ch == '\b') {
+
+ if (ch == ' '
+ || ch == ','
+ || ch == '}'
+ || ch == ']'
+ || ch == '\n'
+ || ch == '\r'
+ || ch == '\t'
+ || ch == EOI
+ || (ch == ':' && acceptColon)
+ || ch == '\f'
+ || ch == '\b') {
+
token = JSONToken.NULL;
} else {
throw new JSONException("scan null error");
@@ -4543,8 +4723,16 @@ public final void scanNullOrNew() {
}
next();
- if (ch == ' ' || ch == ',' || ch == '}' || ch == ']' || ch == '\n' || ch == '\r' || ch == '\t' || ch == EOI
- || ch == '\f' || ch == '\b') {
+ if (ch == ' ' ||
+ ch == ',' ||
+ ch == '}' ||
+ ch == ']' ||
+ ch == '\n' ||
+ ch == '\r' ||
+ ch == '\t' ||
+ ch == EOI ||
+ ch == '\f' ||
+ ch == '\b') {
token = JSONToken.NEW;
} else {
throw new JSONException("scan new error");
@@ -4577,8 +4765,18 @@ public final void scanFalse() {
}
next();
- if (ch == ' ' || ch == ',' || ch == '}' || ch == ']' || ch == '\n' || ch == '\r' || ch == '\t' || ch == EOI
- || ch == '\f' || ch == '\b' || ch == ':' || ch == '/') {
+ if (ch == ' ' ||
+ ch == ',' ||
+ ch == '}' ||
+ ch == ']' ||
+ ch == '\n' ||
+ ch == '\r' ||
+ ch == '\t' ||
+ ch == EOI ||
+ ch == '\f' ||
+ ch == '\b' ||
+ ch == ':' ||
+ ch == '/') {
token = JSONToken.FALSE;
} else {
throw new JSONException("scan false error");
@@ -4732,7 +4930,12 @@ public boolean isBlankInput() {
public final void skipWhitespace() {
for (;;) {
if (ch <= '/') {
- if (ch == ' ' || ch == '\r' || ch == '\n' || ch == '\t' || ch == '\f' || ch == '\b') {
+ if (ch == ' ' ||
+ ch == '\r' ||
+ ch == '\n' ||
+ ch == '\t' ||
+ ch == '\f' ||
+ ch == '\b') {
next();
continue;
} else if (ch == '/') {
@@ -4840,7 +5043,20 @@ private void scanStringSingleQuote() {
putChar('\\');
break;
case 'x':
- putChar((char) (digits[next()] * 16 + digits[next()]));
+ char x1 = next();
+ char x2 = next();
+
+ boolean hex1 = (x1 >= '0' && x1 <= '9')
+ || (x1 >= 'a' && x1 <= 'f')
+ || (x1 >= 'A' && x1 <= 'F');
+ boolean hex2 = (x2 >= '0' && x2 <= '9')
+ || (x2 >= 'a' && x2 <= 'f')
+ || (x2 >= 'A' && x2 <= 'F');
+ if (!hex1 || !hex2) {
+ throw new JSONException("invalid escape character \\x" + x1 + x2);
+ }
+
+ putChar((char) (digits[x1] * 16 + digits[x2]));
break;
case 'u':
putChar((char) Integer.parseInt(new String(new char[] { next(), next(), next(), next() }), 16));
@@ -4872,8 +5088,12 @@ private void scanStringSingleQuote() {
* Append a character to sbuf.
*/
protected final void putChar(char ch) {
- if (sp == sbuf.length) {
- char[] newsbuf = new char[sbuf.length * 2];
+ if (sp >= sbuf.length) {
+ int len = sbuf.length * 2;
+ if (len < sp) {
+ len = sp + 1;
+ }
+ char[] newsbuf = new char[len];
System.arraycopy(sbuf, 0, newsbuf, 0, sbuf.length);
sbuf = newsbuf;
}
@@ -4892,6 +5112,12 @@ public final void scanHex() {
np = bp;
next();
+ if (ch == '\'') {
+ next();
+ token = JSONToken.HEX;
+ return;
+ }
+
for (int i = 0;;++i) {
char ch = next();
if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F')) {
@@ -4942,6 +5168,10 @@ public final void scanNumber() {
}
}
+ if (sp > 65535) {
+ throw new JSONException("scanNumber overflow");
+ }
+
if (ch == 'L') {
sp++;
next();
@@ -5071,7 +5301,13 @@ public final Number decimalValue(boolean decimal) {
public static boolean isWhitespace(char ch) {
// 专门调整了判断顺序
- return ch <= ' ' && (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '\f' || ch == '\b');
+ return ch <= ' ' &&
+ (ch == ' ' ||
+ ch == '\n' ||
+ ch == '\r' ||
+ ch == '\t' ||
+ ch == '\f' ||
+ ch == '\b');
}
protected static final long MULTMIN_RADIX_TEN = Long.MIN_VALUE / 10;
@@ -5110,4 +5346,8 @@ public boolean matchField2(char[] fieldName) {
public int getFeatures() {
return this.features;
}
+
+ public void setFeatures(int features) {
+ this.features = features;
+ }
}
diff --git a/src/main/java/com/alibaba/fastjson/parser/JSONReaderScanner.java b/src/main/java/com/alibaba/fastjson/parser/JSONReaderScanner.java
index 06165bb798..4781c9916a 100644
--- a/src/main/java/com/alibaba/fastjson/parser/JSONReaderScanner.java
+++ b/src/main/java/com/alibaba/fastjson/parser/JSONReaderScanner.java
@@ -20,6 +20,7 @@
import java.io.Reader;
import java.io.StringReader;
import java.math.BigDecimal;
+import java.math.MathContext;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
@@ -296,7 +297,11 @@ public final BigDecimal decimalValue() {
sp--;
}
- return new BigDecimal(buf, offset, sp);
+ if (sp > 65535) {
+ throw new JSONException("decimal overflow");
+ }
+
+ return new BigDecimal(buf, offset, sp, MathContext.UNLIMITED);
}
public void close() {
@@ -312,7 +317,7 @@ public void close() {
@Override
public boolean isEOF() {
- return bufLength == -1 || bp == buf.length || ch == EOI && bp + 1 == buf.length;
+ return bufLength == -1 || bp == buf.length || ch == EOI && bp + 1 >= buf.length;
}
public final boolean isBlankInput() {
diff --git a/src/main/java/com/alibaba/fastjson/parser/JSONScanner.java b/src/main/java/com/alibaba/fastjson/parser/JSONScanner.java
index e1241901cc..c6b26e6be8 100644
--- a/src/main/java/com/alibaba/fastjson/parser/JSONScanner.java
+++ b/src/main/java/com/alibaba/fastjson/parser/JSONScanner.java
@@ -15,20 +15,17 @@
*/
package com.alibaba.fastjson.parser;
-import java.lang.reflect.Type;
-import java.math.BigDecimal;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.TimeZone;
-
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
-import com.alibaba.fastjson.annotation.JSONType;
import com.alibaba.fastjson.util.ASMUtils;
import com.alibaba.fastjson.util.IOUtils;
-import com.alibaba.fastjson.util.TypeUtils;
+
+import java.math.BigDecimal;
+import java.math.MathContext;
+import java.util.*;
+
+import static com.alibaba.fastjson.util.TypeUtils.fnv1a_64_magic_hashcode;
+import static com.alibaba.fastjson.util.TypeUtils.fnv1a_64_magic_prime;
//这个类,为了性能优化做了很多特别处理,一切都是为了性能!!!
@@ -131,7 +128,12 @@ public byte[] bytesValue() {
return bytes;
}
- return IOUtils.decodeBase64(text, np + 1, sp);
+ if (!hasSpecial) {
+ return IOUtils.decodeBase64(text, np + 1, sp);
+ } else {
+ String escapedText = new String(sbuf, 0, sp);
+ return IOUtils.decodeBase64(escapedText);
+ }
}
/**
@@ -190,14 +192,18 @@ public final BigDecimal decimalValue() {
sp--;
}
+ if (sp > 65535) {
+ throw new JSONException("decimal overflow");
+ }
+
int offset = np, count = sp;
if (count < sbuf.length) {
text.getChars(offset, offset + count, sbuf, 0);
- return new BigDecimal(sbuf, 0, count);
+ return new BigDecimal(sbuf, 0, count, MathContext.UNLIMITED);
} else {
char[] chars = new char[count];
text.getChars(offset, offset + count, chars, 0);
- return new BigDecimal(chars);
+ return new BigDecimal(chars, 0, chars.length, MathContext.UNLIMITED);
}
}
@@ -280,6 +286,15 @@ private boolean scanISO8601DateIfMatch(boolean strict, int rest) {
M1 = c6;
d0 = c8;
d1 = charAt(bp + 9);
+ } else if (c4 == '-' && c6 == '-') {
+ y0 = c0;
+ y1 = c1;
+ y2 = c2;
+ y3 = c3;
+ M0 = '0';
+ M1 = c5;
+ d0 = '0';
+ d1 = c7;
} else {
y0 = c0;
y1 = c1;
@@ -378,17 +393,24 @@ private boolean scanISO8601DateIfMatch(boolean strict, int rest) {
char y0, y1, y2, y3, M0, M1, d0, d1;
if ((c4 == '-' && c7 == '-') // cn
|| (c4 == '/' && c7 == '/') // tw yyyy/mm/dd
- ) {
+ ) {
y0 = c0;
y1 = c1;
y2 = c2;
y3 = c3;
M0 = c5;
M1 = c6;
- d0 = c8;
- d1 = c9;
+
+ if (c9 == ' ') {
+ d0 = '0';
+ d1 = c8;
+ date_len = 9;
+ } else {
+ d0 = c8;
+ d1 = c9;
+ }
} else if ((c4 == '-' && c6 == '-') // cn yyyy-m-dd
- ) {
+ ) {
y0 = c0;
y1 = c1;
y2 = c2;
@@ -407,7 +429,7 @@ private boolean scanISO8601DateIfMatch(boolean strict, int rest) {
}
} else if ((c2 == '.' && c5 == '.') // de dd.mm.yyyy
|| (c2 == '-' && c5 == '-') // in dd-mm-yyyy
- ) {
+ ) {
d0 = c0;
d1 = c1;
M0 = c3;
@@ -416,6 +438,16 @@ private boolean scanISO8601DateIfMatch(boolean strict, int rest) {
y1 = c7;
y2 = c8;
y3 = c9;
+ } else if (c8 == 'T') {
+ y0 = c0;
+ y1 = c1;
+ y2 = c2;
+ y3 = c3;
+ M0 = c4;
+ M1 = c5;
+ d0 = c6;
+ d1 = c7;
+ date_len = 8;
} else {
if (c4 == '年' || c4 == '년') {
y0 = c0;
@@ -463,7 +495,32 @@ private boolean scanISO8601DateIfMatch(boolean strict, int rest) {
setCalendar(y0, y1, y2, y3, M0, M1, d0, d1);
char t = charAt(bp + date_len);
- if (t == 'T' || (t == ' ' && !strict)) {
+ if (t == 'T' && rest == 16 && date_len == 8 && charAt(bp + 15) == 'Z') {
+ char h0 = charAt(bp + date_len + 1);
+ char h1 = charAt(bp + date_len + 2);
+ char m0 = charAt(bp + date_len + 3);
+ char m1 = charAt(bp + date_len + 4);
+ char s0 = charAt(bp + date_len + 5);
+ char s1 = charAt(bp + date_len + 6);
+
+ if (!checkTime(h0, h1, m0, m1, s0, s1)) {
+ return false;
+ }
+
+ setTime(h0, h1, m0, m1, s0, s1);
+ calendar.set(Calendar.MILLISECOND, 0);
+
+ if (calendar.getTimeZone().getRawOffset() != 0) {
+ String[] timeZoneIDs = TimeZone.getAvailableIDs(0);
+ if (timeZoneIDs.length > 0) {
+ TimeZone timeZone = TimeZone.getTimeZone(timeZoneIDs[0]);
+ calendar.setTimeZone(timeZone);
+ }
+ }
+
+ token = JSONToken.LITERAL_ISO8601_DATE;
+ return true;
+ } else if (t == 'T' || (t == ' ' && !strict)) {
if (rest < date_len + 9) { // "0000-00-00T00:00:00".length()
return false;
}
@@ -516,57 +573,46 @@ private boolean scanISO8601DateIfMatch(boolean strict, int rest) {
setTime(h0, h1, m0, m1, s0, s1);
char dot = charAt(bp + date_len + 9);
- if (dot == '.') {
- if (rest < date_len + 11) { // // 0000-00-00T00:00:00.000
+ int millisLen = -1; // 有可能没有毫秒区域,没有毫秒区域的时候下一个字符位置有可能是'Z'、'+'、'-'
+ int millis = 0;
+ if (dot == '.') { // 0000-00-00T00:00:00.000
+ if (rest < date_len + 11) {
return false;
}
- } else {
- calendar.set(Calendar.MILLISECOND, 0);
-
- ch = charAt(bp += (date_len + 9));
-
- token = JSONToken.LITERAL_ISO8601_DATE;
- if (dot == 'Z') {// UTC
- // bugfix https://github.com/alibaba/fastjson/issues/376
- if (calendar.getTimeZone().getRawOffset() != 0) {
- String[] timeZoneIDs = TimeZone.getAvailableIDs(0);// 没有+ 和 - 默认相对0
- if (timeZoneIDs.length > 0) {
- TimeZone timeZone = TimeZone.getTimeZone(timeZoneIDs[0]);
- calendar.setTimeZone(timeZone);
- }
- }
+ char S0 = charAt(bp + date_len + 10);
+ if (S0 < '0' || S0 > '9') {
+ return false;
}
- return true;
- }
+ millis = S0 - '0';
+ millisLen = 1;
- char S0 = charAt(bp + date_len + 10);
- if (S0 < '0' || S0 > '9') {
- return false;
- }
- int millis = S0 - '0';
- int millisLen = 1;
-
- if (rest > date_len + 11) {
- char S1 = charAt(bp + date_len + 11);
- if (S1 >= '0' && S1 <= '9') {
- millis = millis * 10 + (S1 - '0');
- millisLen = 2;
+ if (rest > date_len + 11) {
+ char S1 = charAt(bp + date_len + 11);
+ if (S1 >= '0' && S1 <= '9') {
+ millis = millis * 10 + (S1 - '0');
+ millisLen = 2;
+ }
}
- }
- if (millisLen == 2) {
- char S2 = charAt(bp + date_len + 12);
- if (S2 >= '0' && S2 <= '9') {
- millis = millis * 10 + (S2 - '0');
- millisLen = 3;
+ if (millisLen == 2) {
+ char S2 = charAt(bp + date_len + 12);
+ if (S2 >= '0' && S2 <= '9') {
+ millis = millis * 10 + (S2 - '0');
+ millisLen = 3;
+ }
}
}
-
calendar.set(Calendar.MILLISECOND, millis);
int timzeZoneLength = 0;
char timeZoneFlag = charAt(bp + date_len + 10 + millisLen);
+
+ if (timeZoneFlag == ' ') {
+ millisLen++;
+ timeZoneFlag = charAt(bp + date_len + 10 + millisLen);
+ }
+
if (timeZoneFlag == '+' || timeZoneFlag == '-') {
char t0 = charAt(bp + date_len + 10 + millisLen + 1);
if (t0 < '0' || t0 > '1') {
@@ -582,14 +628,34 @@ private boolean scanISO8601DateIfMatch(boolean strict, int rest) {
char t3 = '0', t4 = '0';
if (t2 == ':') { // ThreeLetterISO8601TimeZone
t3 = charAt(bp + date_len + 10 + millisLen + 4);
- if (t3 != '0' && t3 != '3') {
- return false;
- }
-
t4 = charAt(bp + date_len + 10 + millisLen + 5);
- if (t4 != '0') {
- return false;
+
+ if(t3 == '4' && t4 == '5') {
+ // handle some special timezones like xx:45
+
+ if (t0 == '1' && (t1 == '2' || t1 == '3')) {
+ // NZ-CHAT => +12:45
+ // Pacific/Chatham => +12:45
+ // NZ-CHAT => +13:45 (DST)
+ // Pacific/Chatham => +13:45 (DST)
+ } else if (t0 == '0' && (t1 == '5' || t1 == '8')) {
+ // Asia/Kathmandu => +05:45
+ // Asia/Katmandu => +05:45
+ // Australia/Eucla => +08:45
+ } else {
+ return false;
+ }
+ } else {
+ //handle normal timezone like xx:00 and xx:30
+ if (t3 != '0' && t3 != '3') {
+ return false;
+ }
+
+ if (t4 != '0') {
+ return false;
+ }
}
+
timzeZoneLength = 6;
} else if (t2 == '0') { // TwoLetterISO8601TimeZone
t3 = charAt(bp + date_len + 10 + millisLen + 4);
@@ -597,6 +663,14 @@ private boolean scanISO8601DateIfMatch(boolean strict, int rest) {
return false;
}
timzeZoneLength = 5;
+ } else if (t2 == '3' && charAt(bp + date_len + 10 + millisLen + 4) == '0') {
+ t3 = '3';
+ t4 = '0';
+ timzeZoneLength = 5;
+ } else if (t2 == '4' && charAt(bp + date_len + 10 + millisLen + 4) == '5') {
+ t3 = '4';
+ t4 = '5';
+ timzeZoneLength = 5;
} else {
timzeZoneLength = 3;
}
@@ -647,11 +721,7 @@ protected void setTimeZone(char timeZoneFlag, char t0, char t1, char t3, char t4
}
if (calendar.getTimeZone().getRawOffset() != timeZoneOffset) {
- String[] timeZoneIDs = TimeZone.getAvailableIDs(timeZoneOffset);
- if (timeZoneIDs.length > 0) {
- TimeZone timeZone = TimeZone.getTimeZone(timeZoneIDs[0]);
- calendar.setTimeZone(timeZone);
- }
+ calendar.setTimeZone(new SimpleTimeZone(timeZoneOffset, Integer.toString(timeZoneOffset)));
}
}
@@ -711,7 +781,7 @@ private void setCalendar(char y0, char y1, char y2, char y3, char M0, char M1, c
}
static boolean checkDate(char y0, char y1, char y2, char y3, char M0, char M1, int d0, int d1) {
- if (y0 < '1' || y0 > '3') {
+ if (y0 < '0' || y0 > '9') {
return false;
}
if (y1 < '0' || y1 > '9') {
@@ -757,7 +827,7 @@ static boolean checkDate(char y0, char y1, char y2, char y3, char M0, char M1, i
@Override
public boolean isEOF() {
- return bp == len || ch == EOI && bp + 1 == len;
+ return bp == len || (ch == EOI && bp + 1 >= len);
}
public int scanFieldInt(char[] fieldName) {
@@ -791,7 +861,13 @@ public int scanFieldInt(char[] fieldName) {
for (;;) {
ch = charAt(index++);
if (ch >= '0' && ch <= '9') {
- value = value * 10 + (ch - '0');
+ int value_10 = value * 10;
+ if (value_10 < value) {
+ matchStat = NOT_MATCH;
+ return 0;
+ }
+
+ value = value_10 + (ch - '0');
} else if (ch == '.') {
matchStat = NOT_MATCH;
return 0;
@@ -878,10 +954,15 @@ public String scanFieldString(char[] fieldName) {
int startPos = this.bp;
char startChar = this.ch;
+
for (;;) {
if (!charArrayCompare(text, bp, fieldName)) {
if (isWhitespace(ch)) {
next();
+
+ while (isWhitespace(ch)) {
+ next();
+ }
continue;
}
matchStat = NOT_MATCH_NAME;
@@ -893,11 +974,19 @@ public String scanFieldString(char[] fieldName) {
int index = bp + fieldName.length;
+ int spaceCount = 0;
char ch = charAt(index++);
if (ch != '"') {
- matchStat = NOT_MATCH;
+ while (isWhitespace(ch)) {
+ spaceCount++;
+ ch = charAt(index++);
+ }
- return stringDefaultValue();
+ if (ch != '"') {
+ matchStat = NOT_MATCH;
+
+ return stringDefaultValue();
+ }
}
final String strVal;
@@ -925,12 +1014,16 @@ public String scanFieldString(char[] fieldName) {
endIndex = indexOf('"', endIndex + 1);
}
- int chars_len = endIndex - (bp + fieldName.length + 1);
- char[] chars = sub_chars(bp + fieldName.length + 1, chars_len);
+ int chars_len = endIndex - (bp + fieldName.length + 1 + spaceCount);
+ char[] chars = sub_chars(bp + fieldName.length + 1 + spaceCount, chars_len);
stringVal = readString(chars, chars_len);
}
+ if ((this.features & Feature.TrimStringFieldValue.mask) != 0) {
+ stringVal = stringVal.trim();
+ }
+
ch = charAt(endIndex + 1);
for (;;) {
@@ -1100,20 +1193,40 @@ public java.util.Date scanFieldDate(char[] fieldName) {
public long scanFieldSymbol(char[] fieldName) {
matchStat = UNKNOWN;
- if (!charArrayCompare(text, bp, fieldName)) {
- matchStat = NOT_MATCH_NAME;
- return 0;
+ for (;;) {
+ if (!charArrayCompare(text, bp, fieldName)) {
+ if (isWhitespace(ch)) {
+ next();
+
+ while (isWhitespace(ch)) {
+ next();
+ }
+ continue;
+ }
+ matchStat = NOT_MATCH_NAME;
+ return 0;
+ } else {
+ break;
+ }
}
int index = bp + fieldName.length;
-
+ int spaceCount = 0;
char ch = charAt(index++);
if (ch != '"') {
- matchStat = NOT_MATCH;
- return 0;
+ while (isWhitespace(ch)) {
+ ch = charAt(index++);
+ spaceCount++;
+ }
+
+ if (ch != '"') {
+ matchStat = NOT_MATCH;
+
+ return 0;
+ }
}
- long hash = 0xcbf29ce484222325L;
+ long hash = fnv1a_64_magic_hashcode;
for (;;) {
ch = charAt(index++);
if (ch == '\"') {
@@ -1126,7 +1239,7 @@ public long scanFieldSymbol(char[] fieldName) {
}
hash ^= ch;
- hash *= 0x100000001b3L;
+ hash *= fnv1a_64_magic_prime;
}
for (;;) {
@@ -1167,27 +1280,17 @@ public long scanFieldSymbol(char[] fieldName) {
return hash;
}
- public Collection newCollectionByType(Class> type){
- if (type.isAssignableFrom(HashSet.class)) {
- HashSet list = new HashSet();
- return list;
- } else if (type.isAssignableFrom(ArrayList.class)) {
- ArrayList list2 = new ArrayList();
- return list2;
- } else {
- try {
- Collection list = (Collection) type.newInstance();
- return list;
- } catch (Exception e) {
- throw new JSONException(e.getMessage(), e);
- }
- }
- }
-
@SuppressWarnings("unchecked")
public Collection scanFieldStringArray(char[] fieldName, Class> type) {
matchStat = UNKNOWN;
+ while (ch == '\n' || ch == ' ') {
+ int index = ++bp;
+ ch = (index >= this.len ? //
+ EOI //
+ : text.charAt(index));
+ }
+
if (!charArrayCompare(text, bp, fieldName)) {
matchStat = NOT_MATCH_NAME;
return null;
@@ -1207,6 +1310,9 @@ public Collection scanFieldStringArray(char[] fieldName, Class> type)
// }
// }
+ int startPos = this.bp;
+ char startChar = this.ch;
+
int index = bp + fieldName.length;
char ch = charAt(index++);
@@ -1328,6 +1434,8 @@ public Collection scanFieldStringArray(char[] fieldName, Class> type)
matchStat = END;
} else {
+ this.ch = startChar;
+ bp = startPos;
matchStat = NOT_MATCH;
return null;
}
@@ -1517,14 +1625,14 @@ public boolean scanFieldBoolean(char[] fieldName) {
ch = charAt(bp);
value = false;
} else if (ch == '1') {
- if (quote && charAt(index++) != '"') {
- matchStat = NOT_MATCH;
- return false;
- }
+ if (quote && charAt(index++) != '"') {
+ matchStat = NOT_MATCH;
+ return false;
+ }
- bp = index;
- ch = charAt(bp);
- value = true;
+ bp = index;
+ ch = charAt(bp);
+ value = true;
} else if (ch == '0') {
if (quote && charAt(index++) != '"') {
matchStat = NOT_MATCH;
@@ -1586,6 +1694,7 @@ public boolean scanFieldBoolean(char[] fieldName) {
public final int scanInt(char expectNext) {
matchStat = UNKNOWN;
+ final int mark = bp;
int offset = bp;
char chLocal = charAt(offset++);
@@ -1610,7 +1719,12 @@ public final int scanInt(char expectNext) {
for (;;) {
chLocal = charAt(offset++);
if (chLocal >= '0' && chLocal <= '9') {
- value = value * 10 + (chLocal - '0');
+ int value_10 = value * 10;
+ if (value_10 < value) {
+ throw new JSONException("parseInt error : "
+ + subString(mark, offset - 1));
+ }
+ value = value_10 + (chLocal - '0');
} else if (chLocal == '.') {
matchStat = NOT_MATCH;
return 0;
@@ -1767,7 +1881,7 @@ public double scanDouble(char seperator) {
count = offset - start - 1;
}
- if (!exp && count < 20) {
+ if (!exp && count < 18) {
value = ((double) intVal) / power;
if (negative) {
value = -value;
@@ -2059,11 +2173,36 @@ protected final void arrayCopy(int srcPos, char[] dest, int destPos, int length)
}
public String info() {
- return "pos " + bp //
- + ", json : " //
- + (text.length() < 65536 //
- ? text //
- : text.substring(0, 65536));
+ StringBuilder buf = new StringBuilder();
+
+// buf.append("pos ").append(bp);
+// return "pos " + bp //
+// + ", json : " //
+// + (text.length() < 65536 //
+// ? text //
+// : text.substring(0, 65536));
+
+ int line = 1;
+ int column = 1;
+ for (int i = 0; i < bp; ++i, column++) {
+ char ch = text.charAt(i);
+ if (ch == '\n') {
+ column = 1;
+ line++;
+ }
+ }
+
+ buf.append("pos ").append(bp)
+ .append(", line ").append(line)
+ .append(", column ").append(column);
+
+ if (text.length() < 65535) {
+ buf.append(text);
+ } else {
+ buf.append(text.substring(0, 65535));
+ }
+
+ return buf.toString();
}
// for hsf support
@@ -2194,7 +2333,724 @@ public boolean matchField2(char[] fieldName) {
matchStat = NOT_MATCH_NAME;
return false;
}
+ }
+
+ public final void skipObject() {
+ skipObject(false);
+ }
+
+ public final void skipObject(boolean valid) {
+ boolean quote = false;
+ int braceCnt = 0;
+ int i = bp;
+ for (; i < text.length(); ++i) {
+ final char ch = text.charAt(i);
+ if (ch == '\\') {
+ if (i < len - 1) {
+ ++i;
+ continue;
+ } else {
+ this.ch = ch;
+ this.bp = i;
+ throw new JSONException("illegal str, " + info());
+ }
+ } else if (ch == '"') {
+ quote = !quote;
+ } else if (ch == '{') {
+ if (quote) {
+ continue;
+ }
+ braceCnt++;
+ } else if (ch == '}') {
+ if (quote) {
+ continue;
+ } else {
+ braceCnt--;
+ }
+ if (braceCnt == -1) {
+ this.bp = i + 1;
+ if (this.bp == text.length()) {
+ this.ch = EOI;
+ this.token = JSONToken.EOF;
+ return;
+ }
+ this.ch = text.charAt(this.bp);
+ if (this.ch == ',') {
+ token = JSONToken.COMMA;
+ int index = ++bp;
+ this.ch = (index >= text.length() //
+ ? EOI //
+ : text.charAt(index));
+ return;
+ } else if (this.ch == '}') {
+ token = JSONToken.RBRACE;
+ next();
+ return;
+ } else if (this.ch == ']') {
+ token = JSONToken.RBRACKET;
+ next();
+ return;
+ } else {
+ nextToken(JSONToken.COMMA);
+ }
+ return;
+ }
+ }
+ }
+
+ for (int j = 0; j < bp; j++) {
+ if (j < text.length() && text.charAt(j) == ' ') {
+ i++;
+ }
+ }
+
+ if (i == text.length()) {
+ throw new JSONException("illegal str, " + info());
+ }
+ }
+
+ public final void skipArray() {
+ skipArray(false);
+ }
+
+ public final void skipArray(boolean valid) {
+ boolean quote = false;
+ int bracketCnt = 0;
+ int i = bp;
+ for (; i < text.length(); ++i) {
+ char ch = text.charAt(i);
+ if (ch == '\\') {
+ if (i < len - 1) {
+ ++i;
+ continue;
+ } else {
+ this.ch = ch;
+ this.bp = i;
+ throw new JSONException("illegal str, " + info());
+ }
+ } else if (ch == '"') {
+ quote = !quote;
+ } else if (ch == '[') {
+ if (quote) {
+ continue;
+ }
+ bracketCnt++;
+ } else if (ch == '{' && valid) {
+ {
+ int index = ++bp;
+ this.ch = (index >= text.length() //
+ ? EOI //
+ : text.charAt(index));
+ }
+
+ skipObject(valid);
+ } else if (ch == ']') {
+ if (quote) {
+ continue;
+ } else {
+ bracketCnt--;
+ }
+ if (bracketCnt == -1) {
+ this.bp = i + 1;
+ if (this.bp == text.length()) {
+ this.ch = EOI;
+ token = JSONToken.EOF;
+ return;
+ }
+ this.ch = text.charAt(this.bp);
+ nextToken(JSONToken.COMMA);
+ return;
+ }
+ }
+ }
+
+ if (i == text.length()) {
+ throw new JSONException("illegal str, " + info());
+ }
+ }
+
+ public final void skipString() {
+ if (ch == '"') {
+ for (int i = bp + 1; i < text.length(); ++i) {
+ char c = text.charAt(i);
+ if (c == '\\') {
+ if (i < len - 1) {
+ ++i;
+ continue;
+ }
+ } else if (c == '"') {
+ this.ch = text.charAt(bp = i + 1);
+ return;
+ }
+ }
+ throw new JSONException("unclosed str");
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ public boolean seekArrayToItem(int index) {
+ if (index < 0) {
+ throw new IllegalArgumentException("index must > 0, but " + index);
+ }
+
+ if (token == JSONToken.EOF) {
+ return false;
+ }
+ if (token != JSONToken.LBRACKET) {
+ throw new UnsupportedOperationException();
+ }
+// nextToken();
+
+ for (int i = 0; i < index; ++i) {
+ skipWhitespace();
+ if (ch == '"' || ch == '\'') {
+ skipString();
+ if (ch == ',') {
+ next();
+ continue;
+ } else if (ch == ']') {
+ next();
+ nextToken(JSONToken.COMMA);
+ return false;
+ } else {
+ throw new JSONException("illegal json.");
+ }
+ } else if (ch == '{') {
+ next();
+ token = JSONToken.LBRACE;
+ skipObject(false);
+ } else if (ch == '[') {
+ next();
+ token = JSONToken.LBRACKET;
+ skipArray(false);
+ } else {
+ boolean match = false;
+ for (int j = bp + 1; j < text.length(); ++j) {
+ char c = text.charAt(j);
+ if (c == ',') {
+ match = true;
+ bp = j + 1;
+ ch = charAt(bp);
+ break;
+ } else if (c == ']') {
+ bp = j + 1;
+ ch = charAt(bp);
+ nextToken();
+ return false;
+ }
+ }
+
+ if (!match) {
+ throw new JSONException("illegal json.");
+ }
+
+ continue;
+ }
+
+ if (token == JSONToken.COMMA) {
+ continue;
+ } else if (token == JSONToken.RBRACKET) {
+ return false;
+ } else {
+ throw new UnsupportedOperationException();
+ }
+
+ }
+
+ nextToken();
+ return true;
+ }
+
+ public int seekObjectToField(long fieldNameHash, boolean deepScan) {
+ if (token == JSONToken.EOF) {
+ return JSONLexer.NOT_MATCH;
+ }
+
+ if (token == JSONToken.RBRACE || token == JSONToken.RBRACKET) {
+ nextToken();
+ return JSONLexer.NOT_MATCH;
+ }
+
+ if (token != JSONToken.LBRACE && token != JSONToken.COMMA) {
+ throw new UnsupportedOperationException(JSONToken.name(token));
+ }
+
+ for (;;) {
+ if (ch == '}') {
+ next();
+ nextToken();
+ return JSONLexer.NOT_MATCH;
+ }
+ if (ch == EOI) {
+ return JSONLexer.NOT_MATCH;
+ }
+
+ if (ch != '"') {
+ skipWhitespace();
+ }
+
+ long hash;
+ if (ch == '"') {
+ hash = fnv1a_64_magic_hashcode;
+
+ for (int i = bp + 1; i < text.length(); ++i) {
+ char c = text.charAt(i);
+ if (c == '\\') {
+ ++i;
+ if (i == text.length()) {
+ throw new JSONException("unclosed str, " + info());
+ }
+ c = text.charAt(i);
+ }
+
+ if (c == '"') {
+ bp = i + 1;
+ ch = (bp >= text.length() //
+ ? EOI //
+ : text.charAt(bp));
+ break;
+ }
+
+ hash ^= c;
+ hash *= fnv1a_64_magic_prime;
+ }
+ } else {
+ throw new UnsupportedOperationException();
+ }
+
+ if (hash == fieldNameHash) {
+ if (ch != ':') {
+ skipWhitespace();
+ }
+ if (ch == ':') {
+ {
+ int index = ++bp;
+ ch = (index >= text.length() //
+ ? EOI //
+ : text.charAt(index));
+ }
+ if (ch == ',') {
+ {
+ int index = ++bp;
+ ch = (index >= text.length() //
+ ? EOI //
+ : text.charAt(index));
+ }
+ token = JSONToken.COMMA;
+ } else if (ch == ']') {
+ {
+ int index = ++bp;
+ ch = (index >= text.length() //
+ ? EOI //
+ : text.charAt(index));
+ }
+ token = JSONToken.RBRACKET;
+ } else if (ch == '}') {
+ {
+ int index = ++bp;
+ ch = (index >= text.length() //
+ ? EOI //
+ : text.charAt(index));
+ }
+ token = JSONToken.RBRACE;
+ } else if (ch >= '0' && ch <= '9') {
+ sp = 0;
+ pos = bp;
+ scanNumber();
+ } else {
+ nextToken(JSONToken.LITERAL_INT);
+ }
+ }
+ return VALUE;
+ }
+
+ if (ch != ':') {
+ skipWhitespace();
+ }
+
+ if (ch == ':') {
+ int index = ++bp;
+ ch = (index >= text.length() //
+ ? EOI //
+ : text.charAt(index));
+ } else {
+ throw new JSONException("illegal json, " + info());
+ }
+
+ if (ch != '"'
+ && ch != '\''
+ && ch != '{'
+ && ch != '['
+ && ch != '0'
+ && ch != '1'
+ && ch != '2'
+ && ch != '3'
+ && ch != '4'
+ && ch != '5'
+ && ch != '6'
+ && ch != '7'
+ && ch != '8'
+ && ch != '9'
+ && ch != '+'
+ && ch != '-') {
+ skipWhitespace();
+ }
+
+ // skip fieldValues
+ if (ch == '-' || ch == '+' || (ch >= '0' && ch <= '9')) {
+ next();
+ while (ch >= '0' && ch <= '9') {
+ next();
+ }
+
+ // scale
+ if (ch == '.') {
+ next();
+ while (ch >= '0' && ch <= '9') {
+ next();
+ }
+ }
+
+ // exp
+ if (ch == 'E' || ch == 'e') {
+ next();
+ if (ch == '-' || ch == '+') {
+ next();
+ }
+ while (ch >= '0' && ch <= '9') {
+ next();
+ }
+ }
+
+ if (ch != ',') {
+ skipWhitespace();
+ }
+ if (ch == ',') {
+ next();
+ }
+ } else if (ch == '"') {
+ skipString();
+
+ if (ch != ',' && ch != '}') {
+ skipWhitespace();
+ }
+
+ if (ch == ',') {
+ next();
+ }
+ } else if (ch == 't') {
+ next();
+ if (ch == 'r') {
+ next();
+ if (ch == 'u') {
+ next();
+ if (ch == 'e') {
+ next();
+ }
+ }
+ }
+
+ if (ch != ',' && ch != '}') {
+ skipWhitespace();
+ }
+
+ if (ch == ',') {
+ next();
+ }
+ } else if (ch == 'n') {
+ next();
+ if (ch == 'u') {
+ next();
+ if (ch == 'l') {
+ next();
+ if (ch == 'l') {
+ next();
+ }
+ }
+ }
+
+ if (ch != ',' && ch != '}') {
+ skipWhitespace();
+ }
+
+ if (ch == ',') {
+ next();
+ }
+ } else if (ch == 'f') {
+ next();
+ if (ch == 'a') {
+ next();
+ if (ch == 'l') {
+ next();
+ if (ch == 's') {
+ next();
+ if (ch == 'e') {
+ next();
+ }
+ }
+ }
+ }
+
+ if (ch != ',' && ch != '}') {
+ skipWhitespace();
+ }
+
+ if (ch == ',') {
+ next();
+ }
+ } else if (ch == '{') {
+ {
+ int index = ++bp;
+ ch = (index >= text.length() //
+ ? EOI //
+ : text.charAt(index));
+ }
+ if (deepScan) {
+ token = JSONToken.LBRACE;
+ return OBJECT;
+ }
+
+ skipObject(false);
+ if (token == JSONToken.RBRACE) {
+ return JSONLexer.NOT_MATCH;
+ }
+ } else if (ch == '[') {
+ next();
+ if (deepScan) {
+ token = JSONToken.LBRACKET;
+ return ARRAY;
+ }
+ skipArray(false);
+ if (token == JSONToken.RBRACE) {
+ return JSONLexer.NOT_MATCH;
+ }
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
+ }
+
+ public int seekObjectToField(long[] fieldNameHash) {
+ if (token != JSONToken.LBRACE && token != JSONToken.COMMA) {
+ throw new UnsupportedOperationException();
+ }
+
+ for (;;) {
+ if (ch == '}') {
+ next();
+ nextToken();
+ this.matchStat = JSONLexer.NOT_MATCH;
+ return -1;
+ }
+ if (ch == EOI) {
+ this.matchStat = JSONLexer.NOT_MATCH;
+ return -1;
+ }
+
+ if (ch != '"') {
+ skipWhitespace();
+ }
+
+ long hash;
+ if (ch == '"') {
+ hash = fnv1a_64_magic_hashcode;
+
+ for (int i = bp + 1; i < text.length(); ++i) {
+ char c = text.charAt(i);
+ if (c == '\\') {
+ ++i;
+ if (i == text.length()) {
+ throw new JSONException("unclosed str, " + info());
+ }
+ c = text.charAt(i);
+ }
+
+ if (c == '"') {
+ bp = i + 1;
+ ch = (bp >= text.length() //
+ ? EOI //
+ : text.charAt(bp));
+ break;
+ }
+
+ hash ^= c;
+ hash *= fnv1a_64_magic_prime;
+ }
+ } else {
+ throw new UnsupportedOperationException();
+ }
+
+ int matchIndex = -1;
+ for (int i = 0; i < fieldNameHash.length; i++) {
+ if (hash == fieldNameHash[i]) {
+ matchIndex = i;
+ break;
+ }
+ }
+
+ if (matchIndex != -1) {
+ if (ch != ':') {
+ skipWhitespace();
+ }
+ if (ch == ':') {
+ {
+ int index = ++bp;
+ ch = (index >= text.length() //
+ ? EOI //
+ : text.charAt(index));
+ }
+ if (ch == ',') {
+ {
+ int index = ++bp;
+ ch = (index >= text.length() //
+ ? EOI //
+ : text.charAt(index));
+ }
+ token = JSONToken.COMMA;
+ } else if (ch == ']') {
+ {
+ int index = ++bp;
+ ch = (index >= text.length() //
+ ? EOI //
+ : text.charAt(index));
+ }
+ token = JSONToken.RBRACKET;
+ } else if (ch == '}') {
+ {
+ int index = ++bp;
+ ch = (index >= text.length() //
+ ? EOI //
+ : text.charAt(index));
+ }
+ token = JSONToken.RBRACE;
+ } else if (ch >= '0' && ch <= '9') {
+ sp = 0;
+ pos = bp;
+ scanNumber();
+ } else {
+ nextToken(JSONToken.LITERAL_INT);
+ }
+ }
+
+ matchStat = VALUE;
+ return matchIndex;
+ }
+
+ if (ch != ':') {
+ skipWhitespace();
+ }
+
+ if (ch == ':') {
+ int index = ++bp;
+ ch = (index >= text.length() //
+ ? EOI //
+ : text.charAt(index));
+ } else {
+ throw new JSONException("illegal json, " + info());
+ }
+
+ if (ch != '"'
+ && ch != '\''
+ && ch != '{'
+ && ch != '['
+ && ch != '0'
+ && ch != '1'
+ && ch != '2'
+ && ch != '3'
+ && ch != '4'
+ && ch != '5'
+ && ch != '6'
+ && ch != '7'
+ && ch != '8'
+ && ch != '9'
+ && ch != '+'
+ && ch != '-') {
+ skipWhitespace();
+ }
+
+ // skip fieldValues
+ if (ch == '-' || ch == '+' || (ch >= '0' && ch <= '9')) {
+ next();
+ while (ch >= '0' && ch <= '9') {
+ next();
+ }
+
+ // scale
+ if (ch == '.') {
+ next();
+ while (ch >= '0' && ch <= '9') {
+ next();
+ }
+ }
+
+ // exp
+ if (ch == 'E' || ch == 'e') {
+ next();
+ if (ch == '-' || ch == '+') {
+ next();
+ }
+ while (ch >= '0' && ch <= '9') {
+ next();
+ }
+ }
+
+ if (ch != ',') {
+ skipWhitespace();
+ }
+ if (ch == ',') {
+ next();
+ }
+ } else if (ch == '"') {
+ skipString();
+
+ if (ch != ',' && ch != '}') {
+ skipWhitespace();
+ }
+
+ if (ch == ',') {
+ next();
+ }
+ } else if (ch == '{') {
+ {
+ int index = ++bp;
+ ch = (index >= text.length() //
+ ? EOI //
+ : text.charAt(index));
+ }
+
+ skipObject(false);
+ } else if (ch == '[') {
+ next();
+
+ skipArray(false);
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
+ }
+
+ public String scanTypeName(SymbolTable symbolTable) {
+ if (text.startsWith("\"@type\":\"", bp)) {
+ int p = text.indexOf('"', bp + 9);
+ if (p != -1) {
+ bp += 9;
+ int h = 0;
+ for (int i = bp; i < p; i++) {
+ h = 31 * h + text.charAt(i);
+ }
+ String typeName = addSymbol(bp, p - bp, h, symbolTable);
+ char separator = text.charAt(p + 1);
+ if (separator != ',' && separator != ']') {
+ return null;
+ }
+ bp = p + 2;
+ ch = text.charAt(bp);
+ return typeName;
+ }
+ }
+ return null;
}
}
diff --git a/src/main/java/com/alibaba/fastjson/parser/JSONToken.java b/src/main/java/com/alibaba/fastjson/parser/JSONToken.java
old mode 100755
new mode 100644
diff --git a/src/main/java/com/alibaba/fastjson/parser/ParseContext.java b/src/main/java/com/alibaba/fastjson/parser/ParseContext.java
old mode 100755
new mode 100644
index 2b215cdaa5..2b43cecb54
--- a/src/main/java/com/alibaba/fastjson/parser/ParseContext.java
+++ b/src/main/java/com/alibaba/fastjson/parser/ParseContext.java
@@ -7,6 +7,7 @@ public class ParseContext {
public Object object;
public final ParseContext parent;
public final Object fieldName;
+ public final int level;
public Type type;
private transient String path;
@@ -14,6 +15,7 @@ public ParseContext(ParseContext parent, Object object, Object fieldName){
this.parent = parent;
this.object = object;
this.fieldName = fieldName;
+ this.level = parent == null ? 0 : parent.level + 1;
}
public String toString() {
diff --git a/src/main/java/com/alibaba/fastjson/parser/ParserConfig.java b/src/main/java/com/alibaba/fastjson/parser/ParserConfig.java
index 8e11868e71..bd11a3dd15 100644
--- a/src/main/java/com/alibaba/fastjson/parser/ParserConfig.java
+++ b/src/main/java/com/alibaba/fastjson/parser/ParserConfig.java
@@ -15,18 +15,10 @@
*/
package com.alibaba.fastjson.parser;
-import java.io.Closeable;
-import java.io.File;
-import java.io.Serializable;
+import java.io.*;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
-import java.lang.reflect.TypeVariable;
-import java.lang.reflect.WildcardType;
+import java.lang.reflect.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.Inet4Address;
@@ -38,23 +30,11 @@
import java.nio.charset.Charset;
import java.security.AccessControlException;
import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Collection;
-import java.util.Currency;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
-import java.util.TimeZone;
-import java.util.TreeMap;
-import java.util.UUID;
+import java.util.*;
+import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicIntegerArray;
@@ -64,29 +44,47 @@
import java.util.regex.Pattern;
import com.alibaba.fastjson.*;
+import com.alibaba.fastjson.annotation.JSONCreator;
import com.alibaba.fastjson.annotation.JSONField;
import com.alibaba.fastjson.annotation.JSONType;
+import com.alibaba.fastjson.asm.ClassReader;
+import com.alibaba.fastjson.asm.TypeCollector;
import com.alibaba.fastjson.parser.deserializer.*;
import com.alibaba.fastjson.serializer.*;
+import com.alibaba.fastjson.spi.Module;
+import com.alibaba.fastjson.support.moneta.MonetaCodec;
import com.alibaba.fastjson.util.*;
+import com.alibaba.fastjson.util.IdentityHashMap;
+import com.alibaba.fastjson.util.ServiceLoader;
-import javax.sql.DataSource;
import javax.xml.datatype.XMLGregorianCalendar;
+import static com.alibaba.fastjson.util.TypeUtils.fnv1a_64_magic_hashcode;
+import static com.alibaba.fastjson.util.TypeUtils.fnv1a_64_magic_prime;
+
/**
* @author wenshao[szujobs@hotmail.com]
*/
public class ParserConfig {
- public final static String DENY_PROPERTY = "fastjson.parser.deny";
- public final static String AUTOTYPE_ACCEPT = "fastjson.parser.autoTypeAccept";
- public final static String AUTOTYPE_SUPPORT_PROPERTY = "fastjson.parser.autoTypeSupport";
-
- public static final String[] DENYS;
+ public static final String DENY_PROPERTY_INTERNAL = "fastjson.parser.deny.internal";
+ public static final String DENY_PROPERTY = "fastjson.parser.deny";
+ public static final String AUTOTYPE_ACCEPT = "fastjson.parser.autoTypeAccept";
+ public static final String AUTOTYPE_SUPPORT_PROPERTY = "fastjson.parser.autoTypeSupport";
+ public static final String SAFE_MODE_PROPERTY = "fastjson.parser.safeMode";
+
+ public static final String[] DENYS_INTERNAL;
+ public static final String[] DENYS;
private static final String[] AUTO_TYPE_ACCEPT_LIST;
- public static final boolean AUTO_SUPPORT;
+ public static final boolean AUTO_SUPPORT;
+ public static final boolean SAFE_MODE;
+ private static final long[] INTERNAL_WHITELIST_HASHCODES;
static {
+ {
+ String property = IOUtils.getStringProperty(DENY_PROPERTY_INTERNAL);
+ DENYS_INTERNAL = splitItemsFormProperty(property);
+ }
{
String property = IOUtils.getStringProperty(DENY_PROPERTY);
DENYS = splitItemsFormProperty(property);
@@ -95,6 +93,10 @@ public class ParserConfig {
String property = IOUtils.getStringProperty(AUTOTYPE_SUPPORT_PROPERTY);
AUTO_SUPPORT = "true".equals(property);
}
+ {
+ String property = IOUtils.getStringProperty(SAFE_MODE_PROPERTY);
+ SAFE_MODE = "true".equals(property);
+ }
{
String property = IOUtils.getStringProperty(AUTOTYPE_ACCEPT);
String[] items = splitItemsFormProperty(property);
@@ -103,20 +105,28 @@ public class ParserConfig {
}
AUTO_TYPE_ACCEPT_LIST = items;
}
+
+ INTERNAL_WHITELIST_HASHCODES = new long[] {
+ 0x9F2E20FB6049A371L,
+ 0xA8AAA929446FFCE4L,
+ 0xD45D6F8C9017FAL,
+ 0x64DC636F343516DCL
+ };
}
public static ParserConfig getGlobalInstance() {
return global;
}
-
public static ParserConfig global = new ParserConfig();
private final IdentityHashMap deserializers = new IdentityHashMap();
+ private final IdentityHashMap> mixInDeserializers = new IdentityHashMap>(16);
+ private final ConcurrentMap> typeMapping = new ConcurrentHashMap>(16, 0.75f, 1);
private boolean asmEnable = !ASMUtils.IS_ANDROID;
public final SymbolTable symbolTable = new SymbolTable(4096);
-
+
public PropertyNamingStrategy propertyNamingStrategy;
protected ClassLoader defaultClassLoader;
@@ -125,14 +135,204 @@ public static ParserConfig getGlobalInstance() {
private static boolean awtError = false;
private static boolean jdk8Error = false;
+ private static boolean jodaError = false;
+ private static boolean guavaError = false;
private boolean autoTypeSupport = AUTO_SUPPORT;
- private String[] denyList = "bsh,com.mchange,com.sun.,java.lang.Thread,java.net.Socket,java.rmi,javax.xml,org.apache.bcel,org.apache.commons.beanutils,org.apache.commons.collections.Transformer,org.apache.commons.collections.functors,org.apache.commons.collections4.comparators,org.apache.commons.fileupload,org.apache.myfaces.context.servlet,org.apache.tomcat,org.apache.wicket.util,org.apache.xalan,org.codehaus.groovy.runtime,org.hibernate,org.jboss,org.mozilla.javascript,org.python.core,org.springframework".split(",");
- private String[] acceptList = AUTO_TYPE_ACCEPT_LIST;
+ private long[] internalDenyHashCodes;
+ private long[] denyHashCodes;
+ private long[] acceptHashCodes;
+
public final boolean fieldBased;
+ private boolean jacksonCompatible = false;
public boolean compatibleWithJavaBean = TypeUtils.compatibleWithJavaBean;
+ private List modules = new ArrayList();
+ private volatile List autoTypeCheckHandlers;
+ private boolean safeMode = SAFE_MODE;
+
+ {
+ denyHashCodes = new long[]{
+ 0x80D0C70BCC2FEA02L,
+ 0x868385095A22725FL,
+ 0x86FC2BF9BEAF7AEFL,
+ 0x87F52A1B07EA33A6L,
+ 0x8872F29FD0B0B7A7L,
+ 0x8BAAEE8F9BF77FA7L,
+ 0x8EADD40CB2A94443L,
+ 0x8F75F9FA0DF03F80L,
+ 0x9172A53F157930AFL,
+ 0x92122D710E364FB8L,
+ 0x941866E73BEFF4C9L,
+ 0x94305C26580F73C5L,
+ 0x9437792831DF7D3FL,
+ 0xA123A62F93178B20L,
+ 0xA85882CE1044C450L,
+ 0xAA3DAFFDB10C4937L,
+ 0xAAA9E6B7C1E1C6A7L,
+ 0xAAAA0826487A3737L,
+ 0xAB82562F53E6E48FL,
+ 0xAC6262F52C98AA39L,
+ 0xAD937A449831E8A0L,
+ 0xAE50DA1FAD60A096L,
+ 0xAFF6FF23388E225AL,
+ 0xAFFF4C95B99A334DL,
+ 0xB40F341C746EC94FL,
+ 0xB7E8ED757F5D13A2L,
+ 0xB98B6B5396932FE9L,
+ 0xBCDD9DC12766F0CEL,
+ 0xBCE0DEE34E726499L,
+ 0xBE4F13E96A6796D0L,
+ 0xBEBA72FB1CCBA426L,
+ 0xC00BE1DEBAF2808BL,
+ 0xC1086AFAE32E6258L,
+ 0xC2664D0958ECFE4CL,
+ 0xC41FF7C9C87C7C05L,
+ 0xC664B363BACA050AL,
+ 0xC7599EBFE3E72406L,
+ 0xC8D49E5601E661A9L,
+ 0xC8F04B3A28909935L,
+ 0xC963695082FD728EL,
+ 0xCBF29CE484222325L,
+ 0xD1EFCDF4B3316D34L,
+ 0xD54B91CC77B239EDL,
+ 0xD59EE91F0B09EA01L,
+ 0xD66F68AB92E7FEF5L,
+ 0xD8CA3D595E982BACL,
+ 0xDCD8D615A6449E3EL,
+ 0xDE23A0809A8B9BD6L,
+ 0xDEFC208F237D4104L,
+ 0xDF2DDFF310CDB375L,
+ 0xE09AE4604842582FL,
+ 0xE1919804D5BF468FL,
+ 0xE2EB3AC7E56C467EL,
+ 0xE603D6A51FAD692BL,
+ 0xE704FD19052B2A34L,
+ 0xE9184BE55B1D962AL,
+ 0xE9F20BAD25F60807L,
+ 0xED13653CB45C4BEDL,
+ 0xF2983D099D29B477L,
+ 0xF3702A4A5490B8E8L,
+ 0xF474E44518F26736L,
+ 0xF4D93F4FB3E3D991L,
+ 0xF5D77DCF8E4D71E6L,
+ 0xF6C0340E73A36A69L,
+ 0xF7E96E74DFA58DBCL,
+ 0xFC773AE20C827691L,
+ 0xFCF3E78644B98BD8L,
+ 0xFD5BFC610056D720L,
+ 0xFFA15BF021F1E37CL,
+ 0xFFDD1A80F1ED3405L,
+ 0x10E067CD55C5E5L,
+ 0x761619136CC13EL,
+ 0x22BAA234C5BFB8AL,
+ 0x3085068CB7201B8L,
+ 0x45B11BC78A3ABA3L,
+ 0x55CFCA0F2281C07L,
+ 0xA555C74FE3A5155L,
+ 0xB6E292FA5955ADEL,
+ 0xBEF8514D0B79293L,
+ 0xEE6511B66FD5EF0L,
+ 0x100150A253996624L,
+ 0x10B2BDCA849D9B3EL,
+ 0x10DBC48446E0DAE5L,
+ 0x119B5B1F10210AFCL,
+ 0x144277B467723158L,
+ 0x14DB2E6FEAD04AF0L,
+ 0x154B6CB22D294CFAL,
+ 0x17924CCA5227622AL,
+ 0x193B2697EAAED41AL,
+ 0x1CD6F11C6A358BB7L,
+ 0x1E0A8C3358FF3DAEL,
+ 0x24652CE717E713BBL,
+ 0x24D2F6048FEF4E49L,
+ 0x24EC99D5E7DC5571L,
+ 0x25E962F1C28F71A2L,
+ 0x275D0732B877AF29L,
+ 0x28AC82E44E933606L,
+ 0x2A71CE2CC40A710CL,
+ 0x2AD1CE3A112F015DL,
+ 0x2ADFEFBBFE29D931L,
+ 0x2B3A37467A344CDFL,
+ 0x2B6DD8B3229D6837L,
+ 0x2D308DBBC851B0D8L,
+ 0x2FE950D3EA52AE0DL,
+ 0x313BB4ABD8D4554CL,
+ 0x327C8ED7C8706905L,
+ 0x332F0B5369A18310L,
+ 0x339A3E0B6BEEBEE9L,
+ 0x33C64B921F523F2FL,
+ 0x33E7F3E02571B153L,
+ 0x34A81EE78429FDF1L,
+ 0x37317698DCFCE894L,
+ 0x378307CB0111E878L,
+ 0x3826F4B2380C8B9BL,
+ 0x398F942E01920CF0L,
+ 0x3A31412DBB05C7FFL,
+ 0x3A7EE0635EB2BC33L,
+ 0x3ADBA40367F73264L,
+ 0x3B0B51ECBF6DB221L,
+ 0x3BF14094A524F0E2L,
+ 0x42D11A560FC9FBA9L,
+ 0x43320DC9D2AE0892L,
+ 0x440E89208F445FB9L,
+ 0x46C808A4B5841F57L,
+ 0x470FD3A18BB39414L,
+ 0x49312BDAFB0077D9L,
+ 0x4A3797B30328202CL,
+ 0x4BA3E254E758D70DL,
+ 0x4BF881E49D37F530L,
+ 0x4CF54EEC05E3E818L,
+ 0x4DA972745FEB30C1L,
+ 0x4EF08C90FF16C675L,
+ 0x4FD10DDC6D13821FL,
+ 0x521B4F573376DF4AL,
+ 0x527DB6B46CE3BCBCL,
+ 0x535E552D6F9700C1L,
+ 0x54855E265FE1DAD5L,
+ 0x5728504A6D454FFCL,
+ 0x599B5C1213A099ACL,
+ 0x5A5BD85C072E5EFEL,
+ 0x5AB0CB3071AB40D1L,
+ 0x5B6149820275EA42L,
+ 0x5D74D3E5B9370476L,
+ 0x5D92E6DDDE40ED84L,
+ 0x5E61093EF8CDDDBBL,
+ 0x5F215622FB630753L,
+ 0x61C5BDD721385107L,
+ 0x62DB241274397C34L,
+ 0x636ECCA2A131B235L,
+ 0x63A220E60A17C7B9L,
+ 0x647AB0224E149EBEL,
+ 0x65F81B84C1D920CDL,
+ 0x665C53C311193973L,
+ 0x6749835432E0F0D2L,
+ 0x69B6E0175084B377L,
+ 0x6A47501EBB2AFDB2L,
+ 0x6FCABF6FA54CAFFFL,
+ 0x6FE92D83FC0A4628L,
+ 0x746BD4A53EC195FBL,
+ 0x74B50BB9260E31FFL,
+ 0x75CC60F5871D0FD3L,
+ 0x767A586A5107FEEFL,
+ 0x78E5935826671397L,
+ 0x793ADDDED7A967F5L,
+ 0x7AA7EE3627A19CF3L,
+ 0x7AFA070241B8CC4BL,
+ 0x7ED9311D28BF1A65L,
+ 0x7ED9481D28BF417AL,
+ 0x7EE6C477DA20BBE3L
+ };
+
+ long[] hashCodes = new long[AUTO_TYPE_ACCEPT_LIST.length];
+ for (int i = 0; i < AUTO_TYPE_ACCEPT_LIST.length; i++) {
+ hashCodes[i] = TypeUtils.fnv1a_64(AUTO_TYPE_ACCEPT_LIST[i]);
+ }
+
+ Arrays.sort(hashCodes);
+ acceptHashCodes = hashCodes;
+ }
public ParserConfig(){
this(false);
@@ -174,11 +374,26 @@ private ParserConfig(ASMDeserializerFactory asmFactory, ClassLoader parentClassL
asmEnable = false;
}
+ initDeserializers();
+
+ addItemsToDeny(DENYS);
+ addItemsToDeny0(DENYS_INTERNAL);
+ addItemsToAccept(AUTO_TYPE_ACCEPT_LIST);
+
+ }
+
+ private final Callable initDeserializersWithJavaSql = new Callable() {
+ public Void call() {
+ deserializers.put(java.sql.Timestamp.class, SqlDateDeserializer.instance_timestamp);
+ deserializers.put(java.sql.Date.class, SqlDateDeserializer.instance);
+ deserializers.put(java.sql.Time.class, TimeDeserializer.instance);
+ deserializers.put(java.util.Date.class, DateCodec.instance);
+ return null;
+ }
+ };
+
+ private void initDeserializers() {
deserializers.put(SimpleDateFormat.class, MiscCodec.instance);
- deserializers.put(java.sql.Timestamp.class, SqlDateDeserializer.instance_timestamp);
- deserializers.put(java.sql.Date.class, SqlDateDeserializer.instance);
- deserializers.put(java.sql.Time.class, TimeDeserializer.instance);
- deserializers.put(java.util.Date.class, DateCodec.instance);
deserializers.put(Calendar.class, CalendarCodec.instance);
deserializers.put(XMLGregorianCalendar.class, CalendarCodec.instance);
@@ -233,7 +448,7 @@ private ParserConfig(ASMDeserializerFactory asmFactory, ClassLoader parentClassL
deserializers.put(TimeZone.class, MiscCodec.instance);
deserializers.put(Locale.class, MiscCodec.instance);
deserializers.put(Currency.class, MiscCodec.instance);
- deserializers.put(InetAddress.class, MiscCodec.instance);
+
deserializers.put(Inet4Address.class, MiscCodec.instance);
deserializers.put(Inet6Address.class, MiscCodec.instance);
deserializers.put(InetSocketAddress.class, MiscCodec.instance);
@@ -254,12 +469,9 @@ private ParserConfig(ASMDeserializerFactory asmFactory, ClassLoader parentClassL
deserializers.put(Closeable.class, JavaObjectDeserializer.instance);
deserializers.put(JSONPObject.class, new JSONPDeserializer());
-
- addItemsToDeny(DENYS);
- addItemsToAccept(AUTO_TYPE_ACCEPT_LIST);
-
+ ModuleUtil.callWhenHasJavaSql(initDeserializersWithJavaSql);
}
-
+
private static String[] splitItemsFormProperty(final String property ){
if (property != null && property.length() > 0) {
return property.split(",");
@@ -287,7 +499,18 @@ public void configFromPropety(Properties properties) {
}
}
}
-
+
+ private void addItemsToDeny0(final String[] items){
+ if (items == null){
+ return;
+ }
+
+ for (int i = 0; i < items.length; ++i) {
+ String item = items[i];
+ this.addDenyInternal(item);
+ }
+ }
+
private void addItemsToDeny(final String[] items){
if (items == null){
return;
@@ -310,6 +533,20 @@ private void addItemsToAccept(final String[] items){
}
}
+ /**
+ * @since 1.2.68
+ */
+ public boolean isSafeMode() {
+ return safeMode;
+ }
+
+ /**
+ * @since 1.2.68
+ */
+ public void setSafeMode(boolean safeMode) {
+ this.safeMode = safeMode;
+ }
+
public boolean isAutoTypeSupport() {
return autoTypeSupport;
}
@@ -326,14 +563,21 @@ public void setAsmEnable(boolean asmEnable) {
this.asmEnable = asmEnable;
}
+ /**
+ * @deprecated
+ */
+ public IdentityHashMap getDerializers() {
+ return deserializers;
+ }
+
public IdentityHashMap getDeserializers() {
return deserializers;
}
public ObjectDeserializer getDeserializer(Type type) {
- ObjectDeserializer derializer = this.deserializers.get(type);
- if (derializer != null) {
- return derializer;
+ ObjectDeserializer deserializer = get(type);
+ if (deserializer != null) {
+ return deserializer;
}
if (type instanceof Class>) {
@@ -362,18 +606,23 @@ public ObjectDeserializer getDeserializer(Type type) {
}
public ObjectDeserializer getDeserializer(Class> clazz, Type type) {
- ObjectDeserializer derializer = deserializers.get(type);
- if (derializer != null) {
- return derializer;
+ ObjectDeserializer deserializer = get(type);
+ if (deserializer == null && type instanceof ParameterizedTypeImpl) {
+ Type innerType = TypeReference.intern((ParameterizedTypeImpl) type);
+ deserializer = get(innerType);
+ }
+
+ if (deserializer != null) {
+ return deserializer;
}
if (type == null) {
type = clazz;
}
- derializer = deserializers.get(type);
- if (derializer != null) {
- return derializer;
+ deserializer = get(type);
+ if (deserializer != null) {
+ return deserializer;
}
{
@@ -387,11 +636,19 @@ public ObjectDeserializer getDeserializer(Class> clazz, Type type) {
}
if (type instanceof WildcardType || type instanceof TypeVariable || type instanceof ParameterizedType) {
- derializer = deserializers.get(clazz);
+ deserializer = get(clazz);
+ }
+
+ if (deserializer != null) {
+ return deserializer;
}
- if (derializer != null) {
- return derializer;
+ for (Module module : modules) {
+ deserializer = module.createDeserializer(this, clazz);
+ if (deserializer != null) {
+ putDeserializer(type, deserializer);
+ return deserializer;
+ }
}
String className = clazz.getName();
@@ -410,8 +667,8 @@ public ObjectDeserializer getDeserializer(Class> clazz, Type type) {
try {
for (String name : names) {
if (name.equals(className)) {
- deserializers.put(Class.forName(name), derializer = AwtCodec.instance);
- return derializer;
+ putDeserializer(Class.forName(name), deserializer = AwtCodec.instance);
+ return deserializer;
}
}
} catch (Throwable e) {
@@ -419,7 +676,7 @@ public ObjectDeserializer getDeserializer(Class> clazz, Type type) {
awtError = true;
}
- derializer = AwtCodec.instance;
+ deserializer = AwtCodec.instance;
}
}
@@ -443,8 +700,8 @@ public ObjectDeserializer getDeserializer(Class> clazz, Type type) {
for (String name : names) {
if (name.equals(className)) {
- deserializers.put(Class.forName(name), derializer = Jdk8DateCodec.instance);
- return derializer;
+ putDeserializer(Class.forName(name), deserializer = Jdk8DateCodec.instance);
+ return deserializer;
}
}
} else if (className.startsWith("java.util.Optional")) {
@@ -456,8 +713,8 @@ public ObjectDeserializer getDeserializer(Class> clazz, Type type) {
};
for (String name : names) {
if (name.equals(className)) {
- deserializers.put(Class.forName(name), derializer = OptionalCodec.instance);
- return derializer;
+ putDeserializer(Class.forName(name), deserializer = OptionalCodec.instance);
+ return deserializer;
}
}
}
@@ -467,12 +724,71 @@ public ObjectDeserializer getDeserializer(Class> clazz, Type type) {
}
}
+ if (!jodaError) {
+ try {
+ if (className.startsWith("org.joda.time.")) {
+ String[] names = new String[] {
+ "org.joda.time.DateTime",
+ "org.joda.time.LocalDate",
+ "org.joda.time.LocalDateTime",
+ "org.joda.time.LocalTime",
+ "org.joda.time.Instant",
+ "org.joda.time.Period",
+ "org.joda.time.Duration",
+ "org.joda.time.DateTimeZone",
+ "org.joda.time.format.DateTimeFormatter"
+ };
+
+ for (String name : names) {
+ if (name.equals(className)) {
+ putDeserializer(Class.forName(name), deserializer = JodaCodec.instance);
+ return deserializer;
+ }
+ }
+ }
+ } catch (Throwable e) {
+ // skip
+ jodaError = true;
+ }
+ }
+
+ if ((!guavaError) //
+ && className.startsWith("com.google.common.collect.")) {
+ try {
+ String[] names = new String[] {
+ "com.google.common.collect.HashMultimap",
+ "com.google.common.collect.LinkedListMultimap",
+ "com.google.common.collect.LinkedHashMultimap",
+ "com.google.common.collect.ArrayListMultimap",
+ "com.google.common.collect.TreeMultimap"
+ };
+
+ for (String name : names) {
+ if (name.equals(className)) {
+ putDeserializer(Class.forName(name), deserializer = GuavaCodec.instance);
+ return deserializer;
+ }
+ }
+ } catch (ClassNotFoundException e) {
+ // skip
+ guavaError = true;
+ }
+ }
+
+ if (className.equals("java.nio.ByteBuffer")) {
+ putDeserializer(clazz, deserializer = ByteBufferCodec.instance);
+ }
+
if (className.equals("java.nio.file.Path")) {
- deserializers.put(clazz, derializer = MiscCodec.instance);
+ putDeserializer(clazz, deserializer = MiscCodec.instance);
}
if (clazz == Map.Entry.class) {
- deserializers.put(clazz, derializer = MiscCodec.instance);
+ putDeserializer(clazz, deserializer = MiscCodec.instance);
+ }
+
+ if (className.equals("org.javamoney.moneta.Money")) {
+ putDeserializer(clazz, deserializer = MonetaCodec.instance);
}
final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
@@ -480,56 +796,122 @@ public ObjectDeserializer getDeserializer(Class> clazz, Type type) {
for (AutowiredObjectDeserializer autowired : ServiceLoader.load(AutowiredObjectDeserializer.class,
classLoader)) {
for (Type forType : autowired.getAutowiredFor()) {
- deserializers.put(forType, autowired);
+ putDeserializer(forType, autowired);
}
}
} catch (Exception ex) {
// skip
}
- if (derializer == null) {
- derializer = deserializers.get(type);
+ if (deserializer == null) {
+ deserializer = get(type);
}
- if (derializer != null) {
- return derializer;
+ if (deserializer != null) {
+ return deserializer;
}
if (clazz.isEnum()) {
+ if (jacksonCompatible) {
+ Method[] methods = clazz.getMethods();
+ for (Method method : methods) {
+ if (TypeUtils.isJacksonCreator(method)) {
+ deserializer = createJavaBeanDeserializer(clazz, type);
+ putDeserializer(type, deserializer);
+ return deserializer;
+ }
+ }
+ }
+
+ Class mixInType = (Class) JSON.getMixInAnnotations(clazz);
+
Class> deserClass = null;
- JSONType jsonType = clazz.getAnnotation(JSONType.class);
+ JSONType jsonType = TypeUtils.getAnnotation(mixInType != null ? mixInType : clazz, JSONType.class);
+
if (jsonType != null) {
deserClass = jsonType.deserializer();
try {
- derializer = (ObjectDeserializer) deserClass.newInstance();
- deserializers.put(clazz, derializer);
- return derializer;
+ deserializer = (ObjectDeserializer) deserClass.newInstance();
+ putDeserializer(clazz, deserializer);
+ return deserializer;
} catch (Throwable error) {
// skip
}
}
- derializer = new EnumDeserializer(clazz);
+ Method jsonCreatorMethod = null;
+ if (mixInType != null) {
+ Method mixedCreator = getEnumCreator(mixInType, clazz);
+ if (mixedCreator != null) {
+ try {
+ jsonCreatorMethod = clazz.getMethod(mixedCreator.getName(), mixedCreator.getParameterTypes());
+ } catch (Exception e) {
+ // skip
+ }
+ }
+ } else {
+ jsonCreatorMethod = getEnumCreator(clazz, clazz);
+ }
+
+ if (jsonCreatorMethod != null) {
+ deserializer = new EnumCreatorDeserializer(jsonCreatorMethod);
+ putDeserializer(clazz, deserializer);
+ return deserializer;
+ }
+
+ deserializer = getEnumDeserializer(clazz);
} else if (clazz.isArray()) {
- derializer = ObjectArrayCodec.instance;
+ deserializer = ObjectArrayCodec.instance;
} else if (clazz == Set.class || clazz == HashSet.class || clazz == Collection.class || clazz == List.class
|| clazz == ArrayList.class) {
- derializer = CollectionCodec.instance;
+ deserializer = CollectionCodec.instance;
} else if (Collection.class.isAssignableFrom(clazz)) {
- derializer = CollectionCodec.instance;
+ deserializer = CollectionCodec.instance;
} else if (Map.class.isAssignableFrom(clazz)) {
- derializer = MapDeserializer.instance;
+ deserializer = MapDeserializer.instance;
} else if (Throwable.class.isAssignableFrom(clazz)) {
- derializer = new ThrowableDeserializer(this, clazz);
+ deserializer = new ThrowableDeserializer(this, clazz);
} else if (PropertyProcessable.class.isAssignableFrom(clazz)) {
- derializer = new PropertyProcessableDeserializer((Class)clazz);
+ deserializer = new PropertyProcessableDeserializer((Class) clazz);
+ } else if (clazz == InetAddress.class) {
+ deserializer = MiscCodec.instance;
} else {
- derializer = createJavaBeanDeserializer(clazz, type);
+ deserializer = createJavaBeanDeserializer(clazz, type);
}
- putDeserializer(type, derializer);
+ putDeserializer(type, deserializer);
- return derializer;
+ return deserializer;
+ }
+
+ private static Method getEnumCreator(Class clazz, Class enumClass) {
+ Method[] methods = clazz.getMethods();
+ Method jsonCreatorMethod = null;
+ for (Method method : methods) {
+ if (Modifier.isStatic(method.getModifiers())
+ && method.getReturnType() == enumClass
+ && method.getParameterTypes().length == 1
+ ) {
+ JSONCreator jsonCreator = method.getAnnotation(JSONCreator.class);
+ if (jsonCreator != null) {
+ jsonCreatorMethod = method;
+ break;
+ }
+ }
+ }
+
+ return jsonCreatorMethod;
+ }
+
+ /**
+ * 可以通过重写这个方法,定义自己的枚举反序列化实现
+ * @param clazz 转换的类型
+ * @return 返回一个枚举的反序列化实现
+ * @author zhu.xiaojie
+ * @time 2020-4-5
+ */
+ protected ObjectDeserializer getEnumDeserializer(Class> clazz){
+ return new EnumDeserializer(clazz);
}
/**
@@ -567,8 +949,9 @@ public ObjectDeserializer createJavaBeanDeserializer(Class> clazz, Type type)
// skip
}
}
-
- asmEnable = jsonType.asm();
+
+ asmEnable = jsonType.asm()
+ && jsonType.parseFeatures().length == 0;
}
if (asmEnable) {
@@ -607,7 +990,13 @@ public ObjectDeserializer createJavaBeanDeserializer(Class> clazz, Type type)
if (clazz.isInterface()) {
asmEnable = false;
}
- JavaBeanInfo beanInfo = JavaBeanInfo.build(clazz, type, propertyNamingStrategy);
+ JavaBeanInfo beanInfo = JavaBeanInfo.build(clazz
+ , type
+ , propertyNamingStrategy
+ ,false
+ , TypeUtils.compatibleWithJavaBean
+ , jacksonCompatible
+ );
if (asmEnable && beanInfo.fields.length > 200) {
asmEnable = false;
@@ -646,6 +1035,7 @@ public ObjectDeserializer createJavaBeanDeserializer(Class> clazz, Type type)
&& ((!ASMUtils.checkName(annotation.name())) //
|| annotation.format().length() != 0 //
|| annotation.deserializeUsing() != Void.class //
+ || annotation.parseFeatures().length != 0 //
|| annotation.unwrapped())
|| (fieldInfo.method != null && fieldInfo.method.getParameterTypes().length > 1)) {
asmEnable = false;
@@ -668,6 +1058,12 @@ public ObjectDeserializer createJavaBeanDeserializer(Class> clazz, Type type)
}
}
+ if (asmEnable) {
+ if (TypeUtils.isXmlField(clazz)) {
+ asmEnable = false;
+ }
+ }
+
if (!asmEnable) {
return new JavaBeanDeserializer(this, clazz, type);
}
@@ -710,7 +1106,30 @@ public FieldDeserializer createFieldDeserializer(ParserConfig mapping, //
}
public void putDeserializer(Type type, ObjectDeserializer deserializer) {
- deserializers.put(type, deserializer);
+ Type mixin = JSON.getMixInAnnotations(type);
+ if (mixin != null) {
+ IdentityHashMap mixInClasses = this.mixInDeserializers.get(type);
+ if (mixInClasses == null) {
+ //多线程下可能会重复创建,但不影响正确性
+ mixInClasses = new IdentityHashMap(4);
+ this.mixInDeserializers.put(type, mixInClasses);
+ }
+ mixInClasses.put(mixin, deserializer);
+ } else {
+ this.deserializers.put(type, deserializer);
+ }
+ }
+
+ public ObjectDeserializer get(Type type) {
+ Type mixin = JSON.getMixInAnnotations(type);
+ if (null == mixin) {
+ return this.deserializers.get(type);
+ }
+ IdentityHashMap mixInClasses = this.mixInDeserializers.get(type);
+ if (mixInClasses == null) {
+ return null;
+ }
+ return mixInClasses.get(mixin);
}
public ObjectDeserializer getDeserializer(FieldInfo fieldInfo) {
@@ -724,33 +1143,42 @@ public boolean isPrimitive(Class> clazz) {
return isPrimitive2(clazz);
}
+ private static Function, Boolean> isPrimitiveFuncation = new Function, Boolean>() {
+ public Boolean apply(Class> clazz) {
+ return clazz == java.sql.Date.class //
+ || clazz == java.sql.Time.class //
+ || clazz == java.sql.Timestamp.class;
+ }
+ };
+
/**
* @deprecated internal method, dont call
*/
- public static boolean isPrimitive2(Class> clazz) {
- return clazz.isPrimitive() //
- || clazz == Boolean.class //
- || clazz == Character.class //
- || clazz == Byte.class //
- || clazz == Short.class //
- || clazz == Integer.class //
- || clazz == Long.class //
- || clazz == Float.class //
- || clazz == Double.class //
- || clazz == BigInteger.class //
- || clazz == BigDecimal.class //
- || clazz == String.class //
- || clazz == java.util.Date.class //
- || clazz == java.sql.Date.class //
- || clazz == java.sql.Time.class //
- || clazz == java.sql.Timestamp.class //
- || clazz.isEnum() //
- ;
- }
-
+ public static boolean isPrimitive2(final Class> clazz) {
+ Boolean primitive = clazz.isPrimitive() //
+ || clazz == Boolean.class //
+ || clazz == Character.class //
+ || clazz == Byte.class //
+ || clazz == Short.class //
+ || clazz == Integer.class //
+ || clazz == Long.class //
+ || clazz == Float.class //
+ || clazz == Double.class //
+ || clazz == BigInteger.class //
+ || clazz == BigDecimal.class //
+ || clazz == String.class //
+ || clazz == java.util.Date.class //
+ || clazz.isEnum() //
+ ;
+ if (!primitive) {
+ primitive = ModuleUtil.callWhenHasJavaSql(isPrimitiveFuncation, clazz);
+ }
+ return primitive != null ? primitive : false;
+ }
+
/**
* fieldName,field ,先生成fieldName的快照,减少之后的findField的轮询
- *
+ *
* @param clazz
* @param fieldCacheMap :map<fieldName ,Field>
*/
@@ -766,7 +1194,7 @@ public static void parserAllFieldToCache(Class> clazz,Map**fieldName*/Strin
parserAllFieldToCache(clazz.getSuperclass(), fieldCacheMap);
}
}
-
+
public static Field getFieldFromCache(String fieldName, Map fieldCacheMap) {
Field field = fieldCacheMap.get(fieldName);
@@ -789,8 +1217,7 @@ public static Field getFieldFromCache(String fieldName, Map field
if (fieldName.length() > 2) {
char c1 = fieldName.charAt(1);
- if (fieldName.length() > 2
- && c0 >= 'a' && c0 <= 'z'
+ if (c0 >= 'a' && c0 <= 'z'
&& c1 >= 'A' && c1 <= 'Z') {
for (Map.Entry entry : fieldCacheMap.entrySet()) {
if (fieldName.equalsIgnoreCase(entry.getKey())) {
@@ -813,21 +1240,43 @@ public void setDefaultClassLoader(ClassLoader defaultClassLoader) {
this.defaultClassLoader = defaultClassLoader;
}
+ public void addDenyInternal(String name) {
+ if (name == null || name.length() == 0) {
+ return;
+ }
+
+ long hash = TypeUtils.fnv1a_64(name);
+ if (internalDenyHashCodes == null) {
+ this.internalDenyHashCodes = new long[] {hash};
+ return;
+ }
+
+ if (Arrays.binarySearch(this.internalDenyHashCodes, hash) >= 0) {
+ return;
+ }
+
+ long[] hashCodes = new long[this.internalDenyHashCodes.length + 1];
+ hashCodes[hashCodes.length - 1] = hash;
+ System.arraycopy(this.internalDenyHashCodes, 0, hashCodes, 0, this.internalDenyHashCodes.length);
+ Arrays.sort(hashCodes);
+ this.internalDenyHashCodes = hashCodes;
+ }
+
public void addDeny(String name) {
if (name == null || name.length() == 0) {
return;
}
- for (String item : denyList) {
- if (name.equals(item)) {
- return; // skip duplication
- }
+ long hash = TypeUtils.fnv1a_64(name);
+ if (Arrays.binarySearch(this.denyHashCodes, hash) >= 0) {
+ return;
}
- String[] denyList = new String[this.denyList.length + 1];
- System.arraycopy(this.denyList, 0, denyList, 0, this.denyList.length);
- denyList[denyList.length - 1] = name;
- this.denyList = denyList;
+ long[] hashCodes = new long[this.denyHashCodes.length + 1];
+ hashCodes[hashCodes.length - 1] = hash;
+ System.arraycopy(this.denyHashCodes, 0, hashCodes, 0, this.denyHashCodes.length);
+ Arrays.sort(hashCodes);
+ this.denyHashCodes = hashCodes;
}
public void addAccept(String name) {
@@ -835,16 +1284,24 @@ public void addAccept(String name) {
return;
}
- for (String item : acceptList) {
- if (name.equals(item)) {
- return; // skip duplication
- }
+ long hash = TypeUtils.fnv1a_64(name);
+ if (Arrays.binarySearch(this.acceptHashCodes, hash) >= 0) {
+ return;
+ }
+
+ long[] hashCodes = new long[this.acceptHashCodes.length + 1];
+ hashCodes[hashCodes.length - 1] = hash;
+ System.arraycopy(this.acceptHashCodes, 0, hashCodes, 0, this.acceptHashCodes.length);
+ Arrays.sort(hashCodes);
+ this.acceptHashCodes = hashCodes;
+ }
+
+ public Class> checkAutoType(Class type) {
+ if (get(type) != null) {
+ return type;
}
- String[] acceptList = new String[this.acceptList.length + 1];
- System.arraycopy(this.acceptList, 0, acceptList, 0, this.acceptList.length);
- acceptList[acceptList.length - 1] = name;
- this.acceptList = acceptList;
+ return checkAutoType(type.getName(), null, JSON.DEFAULT_PARSER_FEATURE);
}
public Class> checkAutoType(String typeName, Class> expectClass) {
@@ -856,43 +1313,130 @@ public Class> checkAutoType(String typeName, Class> expectClass, int feature
return null;
}
- if (typeName.length() >= 128) {
+ if (autoTypeCheckHandlers != null) {
+ for (AutoTypeCheckHandler h : autoTypeCheckHandlers) {
+ Class> type = h.handler(typeName, expectClass, features);
+ if (type != null) {
+ return type;
+ }
+ }
+ }
+
+ final int safeModeMask = Feature.SafeMode.mask;
+ boolean safeMode = this.safeMode
+ || (features & safeModeMask) != 0
+ || (JSON.DEFAULT_PARSER_FEATURE & safeModeMask) != 0;
+ if (safeMode) {
+ throw new JSONException("safeMode not support autoType : " + typeName);
+ }
+
+ final int mask = Feature.SupportAutoType.mask;
+ boolean autoTypeSupport = this.autoTypeSupport
+ || (features & mask) != 0
+ || (JSON.DEFAULT_PARSER_FEATURE & mask) != 0;
+
+ if (typeName.length() >= 192 || typeName.length() < 3) {
+ throw new JSONException("autoType is not support. " + typeName);
+ }
+
+ final boolean expectClassFlag;
+ if (expectClass == null) {
+ expectClassFlag = false;
+ } else {
+ long expectHash = TypeUtils.fnv1a_64(expectClass.getName());
+ if (expectHash == 0x90a25f5baa21529eL
+ || expectHash == 0x2d10a5801b9d6136L
+ || expectHash == 0xaf586a571e302c6bL
+ || expectHash == 0xed007300a7b227c6L
+ || expectHash == 0x295c4605fd1eaa95L
+ || expectHash == 0x47ef269aadc650b4L
+ || expectHash == 0x6439c4dff712ae8bL
+ || expectHash == 0xe3dd9875a2dc5283L
+ || expectHash == 0xe2a8ddba03e69e0dL
+ || expectHash == 0xd734ceb4c3e9d1daL
+ ) {
+ expectClassFlag = false;
+ } else {
+ expectClassFlag = true;
+ }
+ }
+
+ String className = typeName.replace('$', '.');
+ Class> clazz;
+
+ final long h1 = (fnv1a_64_magic_hashcode ^ className.charAt(0)) * fnv1a_64_magic_prime;
+ if (h1 == 0xaf64164c86024f1aL) { // [
throw new JSONException("autoType is not support. " + typeName);
}
- final String className = typeName.replace('$', '.');
- Class> clazz = null;
+ if ((h1 ^ className.charAt(className.length() - 1)) * fnv1a_64_magic_prime == 0x9198507b5af98f0L) {
+ throw new JSONException("autoType is not support. " + typeName);
+ }
+
+ final long h3 = (((((fnv1a_64_magic_hashcode ^ className.charAt(0))
+ * fnv1a_64_magic_prime)
+ ^ className.charAt(1))
+ * fnv1a_64_magic_prime)
+ ^ className.charAt(2))
+ * fnv1a_64_magic_prime;
+
+ long fullHash = TypeUtils.fnv1a_64(className);
+ boolean internalWhite = Arrays.binarySearch(INTERNAL_WHITELIST_HASHCODES, fullHash) >= 0;
+
+ if (internalDenyHashCodes != null) {
+ long hash = h3;
+ for (int i = 3; i < className.length(); ++i) {
+ hash ^= className.charAt(i);
+ hash *= fnv1a_64_magic_prime;
+ if (Arrays.binarySearch(internalDenyHashCodes, hash) >= 0) {
+ throw new JSONException("autoType is not support. " + typeName);
+ }
+ }
+ }
- if (autoTypeSupport || expectClass != null) {
- for (int i = 0; i < acceptList.length; ++i) {
- String accept = acceptList[i];
- if (className.startsWith(accept)) {
- clazz = TypeUtils.loadClass(typeName, defaultClassLoader, false);
+ if ((!internalWhite) && (autoTypeSupport || expectClassFlag)) {
+ long hash = h3;
+ for (int i = 3; i < className.length(); ++i) {
+ hash ^= className.charAt(i);
+ hash *= fnv1a_64_magic_prime;
+ if (Arrays.binarySearch(acceptHashCodes, hash) >= 0) {
+ clazz = TypeUtils.loadClass(typeName, defaultClassLoader, true);
if (clazz != null) {
return clazz;
}
}
- }
+ if (Arrays.binarySearch(denyHashCodes, hash) >= 0 && TypeUtils.getClassFromMapping(typeName) == null) {
+ if (Arrays.binarySearch(acceptHashCodes, fullHash) >= 0) {
+ continue;
+ }
- for (int i = 0; i < denyList.length; ++i) {
- String deny = denyList[i];
- if (className.startsWith(deny) && TypeUtils.getClassFromMapping(typeName) == null) {
throw new JSONException("autoType is not support. " + typeName);
}
}
}
+ clazz = TypeUtils.getClassFromMapping(typeName);
+
if (clazz == null) {
- clazz = TypeUtils.getClassFromMapping(typeName);
+ clazz = deserializers.findClass(typeName);
+ }
+
+ if (expectClass == null && clazz != null && Throwable.class.isAssignableFrom(clazz) && !autoTypeSupport) {
+ clazz = null;
}
if (clazz == null) {
- clazz = deserializers.findClass(typeName);
+ clazz = typeMapping.get(typeName);
+ }
+
+ if (internalWhite) {
+ clazz = TypeUtils.loadClass(typeName, defaultClassLoader, true);
}
if (clazz != null) {
if (expectClass != null
&& clazz != java.util.HashMap.class
+ && clazz != java.util.LinkedHashMap.class
&& !expectClass.isAssignableFrom(clazz)) {
throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
}
@@ -901,44 +1445,83 @@ public Class> checkAutoType(String typeName, Class> expectClass, int feature
}
if (!autoTypeSupport) {
- for (int i = 0; i < denyList.length; ++i) {
- String deny = denyList[i];
- if (className.startsWith(deny)) {
+ long hash = h3;
+ for (int i = 3; i < className.length(); ++i) {
+ char c = className.charAt(i);
+ hash ^= c;
+ hash *= fnv1a_64_magic_prime;
+
+ if (Arrays.binarySearch(denyHashCodes, hash) >= 0) {
+ if (typeName.endsWith("Exception") || typeName.endsWith("Error")) {
+ return null;
+ }
+
throw new JSONException("autoType is not support. " + typeName);
}
- }
- for (int i = 0; i < acceptList.length; ++i) {
- String accept = acceptList[i];
- if (className.startsWith(accept)) {
+
+ // white list
+ if (Arrays.binarySearch(acceptHashCodes, hash) >= 0) {
+ clazz = TypeUtils.loadClass(typeName, defaultClassLoader, true);
+
if (clazz == null) {
- clazz = TypeUtils.loadClass(typeName, defaultClassLoader, false);
+ return expectClass;
}
if (expectClass != null && expectClass.isAssignableFrom(clazz)) {
throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
}
+
return clazz;
}
}
}
- if (clazz == null) {
- clazz = TypeUtils.loadClass(typeName, defaultClassLoader, false);
+ boolean jsonType = false;
+ InputStream is = null;
+ try {
+ String resource = typeName.replace('.', '/') + ".class";
+ if (defaultClassLoader != null) {
+ is = defaultClassLoader.getResourceAsStream(resource);
+ } else {
+ is = ParserConfig.class.getClassLoader().getResourceAsStream(resource);
+ }
+ if (is != null) {
+ ClassReader classReader = new ClassReader(is, true);
+ TypeCollector visitor = new TypeCollector("", new Class[0]);
+ classReader.accept(visitor);
+ jsonType = visitor.hasJsonType();
+ }
+ } catch (Exception e) {
+ // skip
+ } finally {
+ IOUtils.close(is);
+ }
+
+ if (autoTypeSupport || jsonType || expectClassFlag) {
+ boolean cacheClass = autoTypeSupport || jsonType;
+ clazz = TypeUtils.loadClass(typeName, defaultClassLoader, cacheClass);
}
if (clazz != null) {
- if (TypeUtils.getAnnotation(clazz,JSONType.class) != null) {
+ if (jsonType) {
+ if (autoTypeSupport) {
+ TypeUtils.addMapping(typeName, clazz);
+ }
return clazz;
}
if (ClassLoader.class.isAssignableFrom(clazz) // classloader is danger
- || DataSource.class.isAssignableFrom(clazz) // dataSource can load jdbc driver
+ || javax.sql.DataSource.class.isAssignableFrom(clazz) // dataSource can load jdbc driver
+ || javax.sql.RowSet.class.isAssignableFrom(clazz) //
) {
throw new JSONException("autoType is not support. " + typeName);
}
if (expectClass != null) {
if (expectClass.isAssignableFrom(clazz)) {
+ if (autoTypeSupport) {
+ TypeUtils.addMapping(typeName, clazz);
+ }
return clazz;
} else {
throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
@@ -951,15 +1534,59 @@ public Class> checkAutoType(String typeName, Class> expectClass, int feature
}
}
- final int mask = Feature.SupportAutoType.mask;
- boolean autoTypeSupport = this.autoTypeSupport
- || (features & mask) != 0
- || (JSON.DEFAULT_PARSER_FEATURE & mask) != 0;
-
if (!autoTypeSupport) {
+ if (typeName.endsWith("Exception") || typeName.endsWith("Error")) {
+ return null;
+ }
+
throw new JSONException("autoType is not support. " + typeName);
}
+ if (clazz != null) {
+ if (autoTypeSupport) {
+ TypeUtils.addMapping(typeName, clazz);
+ }
+ }
+
return clazz;
}
+
+ public void clearDeserializers() {
+ this.deserializers.clear();
+ this.initDeserializers();
+ }
+
+ public boolean isJacksonCompatible() {
+ return jacksonCompatible;
+ }
+
+ public void setJacksonCompatible(boolean jacksonCompatible) {
+ this.jacksonCompatible = jacksonCompatible;
+ }
+
+ public void register(String typeName, Class type) {
+ typeMapping.putIfAbsent(typeName, type);
+ }
+
+ public void register(Module module) {
+ this.modules.add(module);
+ }
+
+ public void addAutoTypeCheckHandler(AutoTypeCheckHandler h) {
+ List autoTypeCheckHandlers = this.autoTypeCheckHandlers;
+ if (autoTypeCheckHandlers == null) {
+ this.autoTypeCheckHandlers
+ = autoTypeCheckHandlers
+ = new CopyOnWriteArrayList();
+ }
+
+ autoTypeCheckHandlers.add(h);
+ }
+
+ /**
+ * @since 1.2.68
+ */
+ public interface AutoTypeCheckHandler {
+ Class> handler(String typeName, Class> expectClass, int features);
+ }
}
diff --git a/src/main/java/com/alibaba/fastjson/parser/SymbolTable.java b/src/main/java/com/alibaba/fastjson/parser/SymbolTable.java
old mode 100755
new mode 100644
diff --git a/src/main/java/com/alibaba/fastjson/parser/deserializer/ASMDeserializerFactory.java b/src/main/java/com/alibaba/fastjson/parser/deserializer/ASMDeserializerFactory.java
old mode 100755
new mode 100644
index 3737505c6f..e2349758cb
--- a/src/main/java/com/alibaba/fastjson/parser/deserializer/ASMDeserializerFactory.java
+++ b/src/main/java/com/alibaba/fastjson/parser/deserializer/ASMDeserializerFactory.java
@@ -37,10 +37,7 @@
import com.alibaba.fastjson.parser.ParseContext;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.parser.SymbolTable;
-import com.alibaba.fastjson.util.ASMClassLoader;
-import com.alibaba.fastjson.util.FieldInfo;
-import com.alibaba.fastjson.util.JavaBeanInfo;
-import com.alibaba.fastjson.util.TypeUtils;
+import com.alibaba.fastjson.util.*;
public class ASMDeserializerFactory implements Opcodes {
@@ -63,15 +60,22 @@ public ObjectDeserializer createJavaBeanDeserializer(ParserConfig config, JavaBe
}
String className = "FastjsonASMDeserializer_" + seed.incrementAndGet() + "_" + clazz.getSimpleName();
- String packageName = ASMDeserializerFactory.class.getPackage().getName();
- String classNameType = packageName.replace('.', '/') + "/" + className;
- String classNameFull = packageName + "." + className;
+ String classNameType;
+ String classNameFull;
+
+ Package pkg = ASMDeserializerFactory.class.getPackage();
+ if (pkg != null) {
+ String packageName = pkg.getName();
+ classNameType = packageName.replace('.', '/') + "/" + className;
+ classNameFull = packageName + "." + className;
+ } else {
+ classNameType = className;
+ classNameFull = className;
+ }
ClassWriter cw = new ClassWriter();
cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, classNameType, type(JavaBeanDeserializer.class), null);
-
-
_init(cw, new Context(classNameType, config, beanInfo, 3));
_createInstance(cw, new Context(classNameType, config, beanInfo, 3));
_deserialze(cw, new Context(classNameType, config, beanInfo, 5));
@@ -110,6 +114,41 @@ private void _deserialzeArrayMapping(ClassWriter cw, Context context) {
defineVarLexer(context, mw);
+ mw.visitVarInsn(ALOAD, context.var("lexer"));
+ mw.visitVarInsn(ALOAD, 1);
+ mw.visitMethodInsn(INVOKEVIRTUAL, DefaultJSONParser, "getSymbolTable", "()" + desc(SymbolTable.class));
+ mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanTypeName", "(" + desc(SymbolTable.class) + ")Ljava/lang/String;");
+ mw.visitVarInsn(ASTORE, context.var("typeName"));
+
+ Label typeNameNotNull_ = new Label();
+ mw.visitVarInsn(ALOAD, context.var("typeName"));
+ mw.visitJumpInsn(IFNULL, typeNameNotNull_);
+
+ mw.visitVarInsn(ALOAD, 1);
+ mw.visitMethodInsn(INVOKEVIRTUAL, DefaultJSONParser, "getConfig", "()" + desc(ParserConfig.class));
+ mw.visitVarInsn(ALOAD, 0);
+ mw.visitFieldInsn(GETFIELD, type(JavaBeanDeserializer.class), "beanInfo", desc(JavaBeanInfo.class));
+ mw.visitVarInsn(ALOAD, context.var("typeName"));
+ mw.visitMethodInsn(INVOKESTATIC, type(JavaBeanDeserializer.class), "getSeeAlso"
+ , "(" + desc(ParserConfig.class) + desc(JavaBeanInfo.class) + "Ljava/lang/String;)" + desc(JavaBeanDeserializer.class));
+ mw.visitVarInsn(ASTORE, context.var("userTypeDeser"));
+ mw.visitVarInsn(ALOAD, context.var("userTypeDeser"));
+ mw.visitTypeInsn(INSTANCEOF, type(JavaBeanDeserializer.class));
+ mw.visitJumpInsn(IFEQ, typeNameNotNull_);
+
+ mw.visitVarInsn(ALOAD, context.var("userTypeDeser"));
+ mw.visitVarInsn(ALOAD, Context.parser);
+ mw.visitVarInsn(ALOAD, 2);
+ mw.visitVarInsn(ALOAD, 3);
+ mw.visitVarInsn(ALOAD, 4);
+ mw.visitMethodInsn(INVOKEVIRTUAL, //
+ type(JavaBeanDeserializer.class), //
+ "deserialzeArrayMapping", //
+ "(L" + DefaultJSONParser + ";Ljava/lang/reflect/Type;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+ mw.visitInsn(ARETURN);
+
+ mw.visitLabel(typeNameNotNull_);
+
_createInstance(context, mw);
FieldInfo[] sortedFieldInfoList = context.beanInfo.sortedFields;
@@ -127,21 +166,21 @@ private void _deserialzeArrayMapping(ClassWriter cw, Context context) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(BIPUSH, seperator);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanInt", "(C)I");
- mw.visitVarInsn(ISTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ISTORE, context.var_asm(fieldInfo));
} else if (fieldClass == Byte.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(BIPUSH, seperator);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanInt", "(C)I");
mw.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;");
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
Label valueNullEnd_ = new Label();
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitFieldInsn(GETFIELD, JSONLexerBase, "matchStat", "I");
mw.visitLdcInsn(com.alibaba.fastjson.parser.JSONLexerBase.VALUE_NULL);
mw.visitJumpInsn(IF_ICMPNE, valueNullEnd_);
mw.visitInsn(ACONST_NULL);
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
mw.visitLabel(valueNullEnd_);
} else if (fieldClass == Short.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
@@ -149,14 +188,14 @@ private void _deserialzeArrayMapping(ClassWriter cw, Context context) {
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanInt", "(C)I");
mw.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;");
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
Label valueNullEnd_ = new Label();
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitFieldInsn(GETFIELD, JSONLexerBase, "matchStat", "I");
mw.visitLdcInsn(com.alibaba.fastjson.parser.JSONLexerBase.VALUE_NULL);
mw.visitJumpInsn(IF_ICMPNE, valueNullEnd_);
mw.visitInsn(ACONST_NULL);
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
mw.visitLabel(valueNullEnd_);
} else if (fieldClass == Integer.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
@@ -164,20 +203,20 @@ private void _deserialzeArrayMapping(ClassWriter cw, Context context) {
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanInt", "(C)I");
mw.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;");
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
Label valueNullEnd_ = new Label();
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitFieldInsn(GETFIELD, JSONLexerBase, "matchStat", "I");
mw.visitLdcInsn(com.alibaba.fastjson.parser.JSONLexerBase.VALUE_NULL);
mw.visitJumpInsn(IF_ICMPNE, valueNullEnd_);
mw.visitInsn(ACONST_NULL);
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
mw.visitLabel(valueNullEnd_);
} else if (fieldClass == long.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(BIPUSH, seperator);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanLong", "(C)J");
- mw.visitVarInsn(LSTORE, context.var(fieldInfo.name + "_asm", 2));
+ mw.visitVarInsn(LSTORE, context.var_asm(fieldInfo, 2));
} else if (fieldClass == Long.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
@@ -185,25 +224,25 @@ private void _deserialzeArrayMapping(ClassWriter cw, Context context) {
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanLong", "(C)J");
mw.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;");
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
Label valueNullEnd_ = new Label();
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitFieldInsn(GETFIELD, JSONLexerBase, "matchStat", "I");
mw.visitLdcInsn(com.alibaba.fastjson.parser.JSONLexerBase.VALUE_NULL);
mw.visitJumpInsn(IF_ICMPNE, valueNullEnd_);
mw.visitInsn(ACONST_NULL);
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
mw.visitLabel(valueNullEnd_);
} else if (fieldClass == boolean.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(BIPUSH, seperator);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanBoolean", "(C)Z");
- mw.visitVarInsn(ISTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ISTORE, context.var_asm(fieldInfo));
} else if (fieldClass == float.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(BIPUSH, seperator);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFloat", "(C)F");
- mw.visitVarInsn(FSTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(FSTORE, context.var_asm(fieldInfo));
} else if (fieldClass == Float.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
@@ -211,21 +250,21 @@ private void _deserialzeArrayMapping(ClassWriter cw, Context context) {
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFloat", "(C)F");
mw.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;");
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
Label valueNullEnd_ = new Label();
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitFieldInsn(GETFIELD, JSONLexerBase, "matchStat", "I");
mw.visitLdcInsn(com.alibaba.fastjson.parser.JSONLexerBase.VALUE_NULL);
mw.visitJumpInsn(IF_ICMPNE, valueNullEnd_);
mw.visitInsn(ACONST_NULL);
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
mw.visitLabel(valueNullEnd_);
} else if (fieldClass == double.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(BIPUSH, seperator);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanDouble", "(C)D");
- mw.visitVarInsn(DSTORE, context.var(fieldInfo.name + "_asm", 2));
+ mw.visitVarInsn(DSTORE, context.var_asm(fieldInfo, 2));
} else if (fieldClass == Double.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
@@ -233,14 +272,14 @@ private void _deserialzeArrayMapping(ClassWriter cw, Context context) {
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanDouble", "(C)D");
mw.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;");
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
Label valueNullEnd_ = new Label();
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitFieldInsn(GETFIELD, JSONLexerBase, "matchStat", "I");
mw.visitLdcInsn(com.alibaba.fastjson.parser.JSONLexerBase.VALUE_NULL);
mw.visitJumpInsn(IF_ICMPNE, valueNullEnd_);
mw.visitInsn(ACONST_NULL);
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
mw.visitLabel(valueNullEnd_);
} else if (fieldClass == char.class) {
@@ -249,30 +288,30 @@ private void _deserialzeArrayMapping(ClassWriter cw, Context context) {
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanString", "(C)Ljava/lang/String;");
mw.visitInsn(ICONST_0);
mw.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "charAt", "(I)C");
- mw.visitVarInsn(ISTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ISTORE, context.var_asm(fieldInfo));
} else if (fieldClass == String.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(BIPUSH, seperator);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanString", "(C)Ljava/lang/String;");
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
} else if (fieldClass == BigDecimal.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(BIPUSH, seperator);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanDecimal", "(C)Ljava/math/BigDecimal;");
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
} else if (fieldClass == java.util.Date.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(BIPUSH, seperator);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanDate", "(C)Ljava/util/Date;");
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
} else if (fieldClass == java.util.UUID.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(BIPUSH, seperator);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanUUID", "(C)Ljava/util/UUID;");
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
} else if (fieldClass.isEnum()) {
Label enumNumIf_ = new Label();
@@ -328,12 +367,15 @@ private void _deserialzeArrayMapping(ClassWriter cw, Context context) {
mw.visitLabel(enumStore_);
mw.visitTypeInsn(CHECKCAST, type(fieldClass)); // cast
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
} else if (Collection.class.isAssignableFrom(fieldClass)) {
Class> itemClass = TypeUtils.getCollectionItemClass(fieldType);
if (itemClass == String.class) {
- if (fieldClass == List.class || fieldClass == Collections.class || fieldClass == ArrayList.class) {
+ if (fieldClass == List.class
+ || fieldClass == Collections.class
+ || fieldClass == ArrayList.class
+ ) {
mw.visitTypeInsn(NEW, type(ArrayList.class));
mw.visitInsn(DUP);
mw.visitMethodInsn(INVOKESPECIAL, type(ArrayList.class), "", "()V");
@@ -342,10 +384,10 @@ private void _deserialzeArrayMapping(ClassWriter cw, Context context) {
mw.visitMethodInsn(INVOKESTATIC, type(TypeUtils.class), "createCollection",
"(Ljava/lang/Class;)Ljava/util/Collection;");
}
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
mw.visitVarInsn(ALOAD, context.var("lexer"));
- mw.visitVarInsn(ALOAD, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ALOAD, context.var_asm(fieldInfo));
mw.visitVarInsn(BIPUSH, seperator);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanStringArray", "(Ljava/util/Collection;C)V");
@@ -355,7 +397,7 @@ private void _deserialzeArrayMapping(ClassWriter cw, Context context) {
mw.visitLdcInsn(com.alibaba.fastjson.parser.JSONLexerBase.VALUE_NULL);
mw.visitJumpInsn(IF_ICMPNE, valueNullEnd_);
mw.visitInsn(ACONST_NULL);
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
mw.visitLabel(valueNullEnd_);
@@ -371,7 +413,7 @@ private void _deserialzeArrayMapping(ClassWriter cw, Context context) {
mw.visitJumpInsn(IF_ICMPEQ, notError_);
mw.visitVarInsn(ALOAD, 1); // DefaultJSONParser
- mw.visitVarInsn(ILOAD, context.var("token"));
+ mw.visitLdcInsn(token);
mw.visitMethodInsn(INVOKEVIRTUAL, DefaultJSONParser, "throwException", "(I)V");
mw.visitLabel(notError_);
@@ -398,7 +440,7 @@ private void _deserialzeArrayMapping(ClassWriter cw, Context context) {
_newCollection(mw, fieldClass, i, false);
mw.visitInsn(DUP);
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
_getCollectionFieldItemDeser(context, mw, fieldInfo, itemClass);
mw.visitVarInsn(ALOAD, 1);
mw.visitLdcInsn(com.alibaba.fastjson.asm.Type.getType(desc(itemClass)));
@@ -424,7 +466,7 @@ private void _deserialzeArrayMapping(ClassWriter cw, Context context) {
"(Ljava/lang/reflect/Type;)Ljava/lang/Object;");
mw.visitTypeInsn(CHECKCAST, type(fieldClass)); // cast
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
} else {
Label objElseIf_ = new Label();
Label objEndIf_ = new Label();
@@ -443,7 +485,7 @@ private void _deserialzeArrayMapping(ClassWriter cw, Context context) {
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanLong", "(C)J");
mw.visitMethodInsn(INVOKESPECIAL, type(java.util.Date.class), "", "(J)V");
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
mw.visitJumpInsn(GOTO, objEndIf_);
}
@@ -613,14 +655,21 @@ private void _deserialze(ClassWriter cw, Context context) {
mw.visitLdcInsn(Feature.SortFeidFastMatch.mask);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "isEnabled", "(I)Z");
- mw.visitJumpInsn(IFEQ, super_);
+ Label continue_ = new Label();
+ mw.visitJumpInsn(IFNE, continue_);
+ mw.visitJumpInsn(GOTO_W, super_);
+ mw.visitLabel(continue_);
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitLdcInsn(context.clazz.getName());
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanType", "(Ljava/lang/String;)I");
mw.visitLdcInsn(com.alibaba.fastjson.parser.JSONLexerBase.NOT_MATCH);
- mw.visitJumpInsn(IF_ICMPEQ, super_);
+
+ Label continue_2 = new Label();
+ mw.visitJumpInsn(IF_ICMPNE, continue_2);
+ mw.visitJumpInsn(GOTO_W, super_);
+ mw.visitLabel(continue_2);
mw.visitVarInsn(ALOAD, 1); // parser
mw.visitMethodInsn(INVOKEVIRTUAL, DefaultJSONParser, "getContext", "()" + desc(ParseContext.class));
@@ -650,7 +699,12 @@ private void _deserialze(ClassWriter cw, Context context) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitFieldInsn(GETFIELD, JSONLexerBase, "matchStat", "I");
mw.visitLdcInsn(com.alibaba.fastjson.parser.JSONLexerBase.END);
- mw.visitJumpInsn(IF_ICMPEQ, return_);
+ //mw.visitJumpInsn(IF_ICMPEQ, return_);
+
+ Label continue_3 = new Label();
+ mw.visitJumpInsn(IF_ICMPNE, continue_3);
+ mw.visitJumpInsn(GOTO_W, return_);
+ mw.visitLabel(continue_3);
mw.visitInsn(ICONST_0); // UNKOWN
mw.visitIntInsn(ISTORE, context.var("matchStat"));
@@ -676,16 +730,16 @@ private void _deserialze(ClassWriter cw, Context context) {
|| fieldClass == short.class //
|| fieldClass == int.class) {
mw.visitInsn(ICONST_0);
- mw.visitVarInsn(ISTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ISTORE, context.var_asm(fieldInfo));
} else if (fieldClass == long.class) {
mw.visitInsn(LCONST_0);
- mw.visitVarInsn(LSTORE, context.var(fieldInfo.name + "_asm", 2));
+ mw.visitVarInsn(LSTORE, context.var_asm(fieldInfo, 2));
} else if (fieldClass == float.class) {
mw.visitInsn(FCONST_0);
- mw.visitVarInsn(FSTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(FSTORE, context.var_asm(fieldInfo));
} else if (fieldClass == double.class) {
mw.visitInsn(DCONST_0);
- mw.visitVarInsn(DSTORE, context.var(fieldInfo.name + "_asm", 2));
+ mw.visitVarInsn(DSTORE, context.var_asm(fieldInfo, 2));
} else {
if (fieldClass == String.class) {
Label flagEnd_ = new Label();
@@ -706,7 +760,7 @@ private void _deserialze(ClassWriter cw, Context context) {
}
mw.visitTypeInsn(CHECKCAST, type(fieldClass)); // cast
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
}
}
@@ -720,222 +774,222 @@ private void _deserialze(ClassWriter cw, Context context) {
if (fieldClass == boolean.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
- mw.visitFieldInsn(GETFIELD, context.className, fieldInfo.name + "_asm_prefix__", "[C");
+ mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldBoolean", "([C)Z");
- mw.visitVarInsn(ISTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ISTORE, context.var_asm(fieldInfo));
} else if (fieldClass == byte.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
- mw.visitFieldInsn(GETFIELD, context.className, fieldInfo.name + "_asm_prefix__", "[C");
+ mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldInt", "([C)I");
- mw.visitVarInsn(ISTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ISTORE, context.var_asm(fieldInfo));
} else if (fieldClass == Byte.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
- mw.visitFieldInsn(GETFIELD, context.className, fieldInfo.name + "_asm_prefix__", "[C");
+ mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldInt", "([C)I");
mw.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;");
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
Label valueNullEnd_ = new Label();
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitFieldInsn(GETFIELD, JSONLexerBase, "matchStat", "I");
mw.visitLdcInsn(com.alibaba.fastjson.parser.JSONLexerBase.VALUE_NULL);
mw.visitJumpInsn(IF_ICMPNE, valueNullEnd_);
mw.visitInsn(ACONST_NULL);
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
mw.visitLabel(valueNullEnd_);
} else if (fieldClass == short.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
- mw.visitFieldInsn(GETFIELD, context.className, fieldInfo.name + "_asm_prefix__", "[C");
+ mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldInt", "([C)I");
- mw.visitVarInsn(ISTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ISTORE, context.var_asm(fieldInfo));
} else if (fieldClass == Short.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
- mw.visitFieldInsn(GETFIELD, context.className, fieldInfo.name + "_asm_prefix__", "[C");
+ mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldInt", "([C)I");
mw.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;");
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
Label valueNullEnd_ = new Label();
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitFieldInsn(GETFIELD, JSONLexerBase, "matchStat", "I");
mw.visitLdcInsn(com.alibaba.fastjson.parser.JSONLexerBase.VALUE_NULL);
mw.visitJumpInsn(IF_ICMPNE, valueNullEnd_);
mw.visitInsn(ACONST_NULL);
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
mw.visitLabel(valueNullEnd_);
} else if (fieldClass == int.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
- mw.visitFieldInsn(GETFIELD, context.className, fieldInfo.name + "_asm_prefix__", "[C");
+ mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldInt", "([C)I");
- mw.visitVarInsn(ISTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ISTORE, context.var_asm(fieldInfo));
} else if (fieldClass == Integer.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
- mw.visitFieldInsn(GETFIELD, context.className, fieldInfo.name + "_asm_prefix__", "[C");
+ mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldInt", "([C)I");
mw.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;");
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
Label valueNullEnd_ = new Label();
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitFieldInsn(GETFIELD, JSONLexerBase, "matchStat", "I");
mw.visitLdcInsn(com.alibaba.fastjson.parser.JSONLexerBase.VALUE_NULL);
mw.visitJumpInsn(IF_ICMPNE, valueNullEnd_);
mw.visitInsn(ACONST_NULL);
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
mw.visitLabel(valueNullEnd_);
} else if (fieldClass == long.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
- mw.visitFieldInsn(GETFIELD, context.className, fieldInfo.name + "_asm_prefix__", "[C");
+ mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldLong", "([C)J");
- mw.visitVarInsn(LSTORE, context.var(fieldInfo.name + "_asm", 2));
+ mw.visitVarInsn(LSTORE, context.var_asm(fieldInfo, 2));
} else if (fieldClass == Long.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
- mw.visitFieldInsn(GETFIELD, context.className, fieldInfo.name + "_asm_prefix__", "[C");
+ mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldLong", "([C)J");
mw.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;");
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
Label valueNullEnd_ = new Label();
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitFieldInsn(GETFIELD, JSONLexerBase, "matchStat", "I");
mw.visitLdcInsn(com.alibaba.fastjson.parser.JSONLexerBase.VALUE_NULL);
mw.visitJumpInsn(IF_ICMPNE, valueNullEnd_);
mw.visitInsn(ACONST_NULL);
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
mw.visitLabel(valueNullEnd_);
} else if (fieldClass == float.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
- mw.visitFieldInsn(GETFIELD, context.className, fieldInfo.name + "_asm_prefix__", "[C");
+ mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldFloat", "([C)F");
- mw.visitVarInsn(FSTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(FSTORE, context.var_asm(fieldInfo));
} else if (fieldClass == Float.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
- mw.visitFieldInsn(GETFIELD, context.className, fieldInfo.name + "_asm_prefix__", "[C");
+ mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldFloat", "([C)F");
mw.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;");
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
Label valueNullEnd_ = new Label();
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitFieldInsn(GETFIELD, JSONLexerBase, "matchStat", "I");
mw.visitLdcInsn(com.alibaba.fastjson.parser.JSONLexerBase.VALUE_NULL);
mw.visitJumpInsn(IF_ICMPNE, valueNullEnd_);
mw.visitInsn(ACONST_NULL);
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
mw.visitLabel(valueNullEnd_);
} else if (fieldClass == double.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
- mw.visitFieldInsn(GETFIELD, context.className, fieldInfo.name + "_asm_prefix__", "[C");
+ mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldDouble", "([C)D");
- mw.visitVarInsn(DSTORE, context.var(fieldInfo.name + "_asm", 2));
+ mw.visitVarInsn(DSTORE, context.var_asm(fieldInfo, 2));
} else if (fieldClass == Double.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
- mw.visitFieldInsn(GETFIELD, context.className, fieldInfo.name + "_asm_prefix__", "[C");
+ mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldDouble", "([C)D");
mw.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;");
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
Label valueNullEnd_ = new Label();
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitFieldInsn(GETFIELD, JSONLexerBase, "matchStat", "I");
mw.visitLdcInsn(com.alibaba.fastjson.parser.JSONLexerBase.VALUE_NULL);
mw.visitJumpInsn(IF_ICMPNE, valueNullEnd_);
mw.visitInsn(ACONST_NULL);
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
mw.visitLabel(valueNullEnd_);
} else if (fieldClass == String.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
- mw.visitFieldInsn(GETFIELD, context.className, fieldInfo.name + "_asm_prefix__", "[C");
+ mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldString", "([C)Ljava/lang/String;");
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
} else if (fieldClass == java.util.Date.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
- mw.visitFieldInsn(GETFIELD, context.className, fieldInfo.name + "_asm_prefix__", "[C");
+ mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldDate", "([C)Ljava/util/Date;");
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
} else if (fieldClass == java.util.UUID.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
- mw.visitFieldInsn(GETFIELD, context.className, fieldInfo.name + "_asm_prefix__", "[C");
+ mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldUUID", "([C)Ljava/util/UUID;");
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
} else if (fieldClass == BigDecimal.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
- mw.visitFieldInsn(GETFIELD, context.className, fieldInfo.name + "_asm_prefix__", "[C");
+ mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldDecimal", "([C)Ljava/math/BigDecimal;");
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
} else if (fieldClass == BigInteger.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
- mw.visitFieldInsn(GETFIELD, context.className, fieldInfo.name + "_asm_prefix__", "[C");
+ mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldBigInteger", "([C)Ljava/math/BigInteger;");
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
} else if (fieldClass == int[].class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
- mw.visitFieldInsn(GETFIELD, context.className, fieldInfo.name + "_asm_prefix__", "[C");
+ mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldIntArray", "([C)[I");
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
} else if (fieldClass == float[].class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
- mw.visitFieldInsn(GETFIELD, context.className, fieldInfo.name + "_asm_prefix__", "[C");
+ mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldFloatArray", "([C)[F");
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
} else if (fieldClass == float[][].class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
- mw.visitFieldInsn(GETFIELD, context.className, fieldInfo.name + "_asm_prefix__", "[C");
+ mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldFloatArray2", "([C)[[F");
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
} else if (fieldClass.isEnum()) {
mw.visitVarInsn(ALOAD, 0);
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
- mw.visitFieldInsn(GETFIELD, context.className, fieldInfo.name + "_asm_prefix__", "[C");
+ mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
_getFieldDeser(context, mw, fieldInfo);
mw.visitMethodInsn(INVOKEVIRTUAL, type(JavaBeanDeserializer.class), "scanEnum"
, "(L" + JSONLexerBase + ";[C" + desc(ObjectDeserializer.class) + ")Ljava/lang/Enum;");
mw.visitTypeInsn(CHECKCAST, type(fieldClass)); // cast
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
// } else if (fieldClass.isEnum()) {
// mw.visitVarInsn(ALOAD, context.var("lexer"));
// mw.visitVarInsn(ALOAD, 0);
-// mw.visitFieldInsn(GETFIELD, context.className, fieldInfo.name + "_asm_prefix__", "[C");
+// mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
// Label enumNull_ = new Label();
// mw.visitInsn(ACONST_NULL);
// mw.visitTypeInsn(CHECKCAST, type(fieldClass)); // cast
-// mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+// mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
//
// mw.visitVarInsn(ALOAD, 1);
//
@@ -955,12 +1009,12 @@ private void _deserialze(ClassWriter cw, Context context) {
// mw.visitVarInsn(ALOAD, context.var(fieldInfo.name + "_asm_enumName"));
// mw.visitMethodInsn(INVOKESTATIC, type(fieldClass), "valueOf",
// "(Ljava/lang/String;)" + desc(fieldClass));
-// mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+// mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
// mw.visitLabel(enumNull_);
} else if (Collection.class.isAssignableFrom(fieldClass)) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
- mw.visitFieldInsn(GETFIELD, context.className, fieldInfo.name + "_asm_prefix__", "[C");
+ mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
Class> itemClass = TypeUtils.getCollectionItemClass(fieldType);
@@ -968,7 +1022,7 @@ private void _deserialze(ClassWriter cw, Context context) {
mw.visitLdcInsn(com.alibaba.fastjson.asm.Type.getType(desc(fieldClass))); // cast
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldStringArray",
"([CLjava/lang/Class;)" + desc(Collection.class));
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
} else {
_deserialze_list_obj(context, mw, reset_, fieldInfo, fieldClass, itemClass, i);
@@ -1120,8 +1174,6 @@ private void _createInstance(Context context, MethodVisitor mw) {
mw.visitInsn(DUP);
mw.visitMethodInsn(INVOKESPECIAL, type(defaultConstructor.getDeclaringClass()), "", "()V");
-
- mw.visitVarInsn(ASTORE, context.var("instance"));
} else {
mw.visitVarInsn(ALOAD, 0);
mw.visitVarInsn(ALOAD, 1);
@@ -1130,8 +1182,9 @@ private void _createInstance(Context context, MethodVisitor mw) {
mw.visitMethodInsn(INVOKESPECIAL, type(JavaBeanDeserializer.class), "createInstance",
"(L" + DefaultJSONParser + ";Ljava/lang/reflect/Type;)Ljava/lang/Object;");
mw.visitTypeInsn(CHECKCAST, type(context.getInstClass())); // cast
- mw.visitVarInsn(ASTORE, context.var("instance"));
}
+
+ mw.visitVarInsn(ASTORE, context.var("instance"));
}
private void _batchSet(Context context, MethodVisitor mw) {
@@ -1161,18 +1214,18 @@ private void _loadAndSet(Context context, MethodVisitor mw, FieldInfo fieldInfo)
if (fieldClass == boolean.class) {
mw.visitVarInsn(ALOAD, context.var("instance"));
- mw.visitVarInsn(ILOAD, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ILOAD, context.var_asm(fieldInfo));
_set(context, mw, fieldInfo);
} else if (fieldClass == byte.class //
|| fieldClass == short.class //
|| fieldClass == int.class //
|| fieldClass == char.class) {
mw.visitVarInsn(ALOAD, context.var("instance"));
- mw.visitVarInsn(ILOAD, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ILOAD, context.var_asm(fieldInfo));
_set(context, mw, fieldInfo);
} else if (fieldClass == long.class) {
mw.visitVarInsn(ALOAD, context.var("instance"));
- mw.visitVarInsn(LLOAD, context.var(fieldInfo.name + "_asm", 2));
+ mw.visitVarInsn(LLOAD, context.var_asm(fieldInfo, 2));
if (fieldInfo.method != null) {
mw.visitMethodInsn(INVOKEVIRTUAL, type(context.getInstClass()), fieldInfo.method.getName(),
desc(fieldInfo.method));
@@ -1185,34 +1238,34 @@ private void _loadAndSet(Context context, MethodVisitor mw, FieldInfo fieldInfo)
}
} else if (fieldClass == float.class) {
mw.visitVarInsn(ALOAD, context.var("instance"));
- mw.visitVarInsn(FLOAD, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(FLOAD, context.var_asm(fieldInfo));
_set(context, mw, fieldInfo);
} else if (fieldClass == double.class) {
mw.visitVarInsn(ALOAD, context.var("instance"));
- mw.visitVarInsn(DLOAD, context.var(fieldInfo.name + "_asm", 2));
+ mw.visitVarInsn(DLOAD, context.var_asm(fieldInfo, 2));
_set(context, mw, fieldInfo);
} else if (fieldClass == String.class) {
mw.visitVarInsn(ALOAD, context.var("instance"));
- mw.visitVarInsn(ALOAD, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ALOAD, context.var_asm(fieldInfo));
_set(context, mw, fieldInfo);
} else if (fieldClass.isEnum()) {
mw.visitVarInsn(ALOAD, context.var("instance"));
- mw.visitVarInsn(ALOAD, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ALOAD, context.var_asm(fieldInfo));
_set(context, mw, fieldInfo);
} else if (Collection.class.isAssignableFrom(fieldClass)) {
mw.visitVarInsn(ALOAD, context.var("instance"));
Type itemType = TypeUtils.getCollectionItemClass(fieldType);
if (itemType == String.class) {
- mw.visitVarInsn(ALOAD, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ALOAD, context.var_asm(fieldInfo));
mw.visitTypeInsn(CHECKCAST, type(fieldClass)); // cast
} else {
- mw.visitVarInsn(ALOAD, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ALOAD, context.var_asm(fieldInfo));
}
_set(context, mw, fieldInfo);
} else {
mw.visitVarInsn(ALOAD, context.var("instance"));
- mw.visitVarInsn(ALOAD, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ALOAD, context.var_asm(fieldInfo));
_set(context, mw, fieldInfo);
}
}
@@ -1313,7 +1366,7 @@ private void _deserialze_list_obj(Context context, MethodVisitor mw, Label reset
mw.visitJumpInsn(IF_ICMPNE, reset_);
_newCollection(mw, fieldClass, i, false);
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
_getCollectionFieldItemDeser(context, mw, fieldInfo, itemType);
mw.visitVarInsn(ALOAD, 1);
@@ -1324,7 +1377,7 @@ private void _deserialze_list_obj(Context context, MethodVisitor mw, Label reset
"(L" + DefaultJSONParser + ";Ljava/lang/reflect/Type;Ljava/lang/Object;)Ljava/lang/Object;");
mw.visitVarInsn(ASTORE, context.var("list_item_value"));
- mw.visitVarInsn(ALOAD, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ALOAD, context.var_asm(fieldInfo));
mw.visitVarInsn(ALOAD, context.var("list_item_value"));
if (fieldClass.isInterface()) {
mw.visitMethodInsn(INVOKEINTERFACE, type(fieldClass), "add", "(Ljava/lang/Object;)Z");
@@ -1340,7 +1393,7 @@ private void _deserialze_list_obj(Context context, MethodVisitor mw, Label reset
_newCollection(mw, fieldClass, i, false);
mw.visitLabel(storeCollection_);
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
boolean isPrimitive = ParserConfig.isPrimitive2(fieldInfo.fieldClass);
_getCollectionFieldItemDeser(context, mw, fieldInfo, itemType);
@@ -1365,7 +1418,7 @@ private void _deserialze_list_obj(Context context, MethodVisitor mw, Label reset
mw.visitVarInsn(ASTORE, context.var("listContext"));
mw.visitVarInsn(ALOAD, 1); // parser
- mw.visitVarInsn(ALOAD, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ALOAD, context.var_asm(fieldInfo));
mw.visitLdcInsn(fieldInfo.name);
mw.visitMethodInsn(INVOKEVIRTUAL, DefaultJSONParser, "setContext",
"(Ljava/lang/Object;Ljava/lang/Object;)" + desc(ParseContext.class));
@@ -1402,7 +1455,7 @@ private void _deserialze_list_obj(Context context, MethodVisitor mw, Label reset
mw.visitIincInsn(context.var("i"), 1);
- mw.visitVarInsn(ALOAD, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ALOAD, context.var_asm(fieldInfo));
mw.visitVarInsn(ALOAD, context.var("list_item_value"));
if (fieldClass.isInterface()) {
mw.visitMethodInsn(INVOKEINTERFACE, type(fieldClass), "add", "(Ljava/lang/Object;)Z");
@@ -1412,7 +1465,7 @@ private void _deserialze_list_obj(Context context, MethodVisitor mw, Label reset
mw.visitInsn(POP);
mw.visitVarInsn(ALOAD, 1);
- mw.visitVarInsn(ALOAD, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ALOAD, context.var_asm(fieldInfo));
mw.visitMethodInsn(INVOKEVIRTUAL, DefaultJSONParser, "checkListResolve", "(Ljava/util/Collection;)V");
mw.visitVarInsn(ALOAD, context.var("lexer"));
@@ -1609,11 +1662,11 @@ private void _deserialze_obj(Context context, MethodVisitor mw, Label reset_, Fi
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
- mw.visitFieldInsn(GETFIELD, context.className, fieldInfo.name + "_asm_prefix__", "[C");
+ mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "matchField", "([C)Z");
mw.visitJumpInsn(IFNE, matched_);
mw.visitInsn(ACONST_NULL);
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
mw.visitJumpInsn(GOTO, _end_if);
@@ -1682,7 +1735,7 @@ private void _deserObject(Context context, MethodVisitor mw, FieldInfo fieldInfo
mw.visitMethodInsn(INVOKEVIRTUAL, type(JavaBeanDeserializer.class), "deserialze",
"(L" + DefaultJSONParser + ";Ljava/lang/reflect/Type;Ljava/lang/Object;I)Ljava/lang/Object;");
mw.visitTypeInsn(CHECKCAST, type(fieldClass)); // cast
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
mw.visitJumpInsn(GOTO, instanceOfEnd_);
@@ -1702,7 +1755,7 @@ private void _deserObject(Context context, MethodVisitor mw, FieldInfo fieldInfo
mw.visitMethodInsn(INVOKEINTERFACE, type(ObjectDeserializer.class), "deserialze",
"(L" + DefaultJSONParser + ";Ljava/lang/reflect/Type;Ljava/lang/Object;)Ljava/lang/Object;");
mw.visitTypeInsn(CHECKCAST, type(fieldClass)); // cast
- mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm"));
+ mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
mw.visitLabel(instanceOfEnd_);
}
@@ -1710,7 +1763,7 @@ private void _deserObject(Context context, MethodVisitor mw, FieldInfo fieldInfo
private void _getFieldDeser(Context context, MethodVisitor mw, FieldInfo fieldInfo) {
Label notNull_ = new Label();
mw.visitVarInsn(ALOAD, 0);
- mw.visitFieldInsn(GETFIELD, context.className, fieldInfo.name + "_asm_deser__", desc(ObjectDeserializer.class));
+ mw.visitFieldInsn(GETFIELD, context.className, context.fieldDeserName(fieldInfo), desc(ObjectDeserializer.class));
mw.visitJumpInsn(IFNONNULL, notNull_);
mw.visitVarInsn(ALOAD, 0);
@@ -1721,12 +1774,12 @@ private void _getFieldDeser(Context context, MethodVisitor mw, FieldInfo fieldIn
mw.visitMethodInsn(INVOKEVIRTUAL, type(ParserConfig.class), "getDeserializer",
"(Ljava/lang/reflect/Type;)" + desc(ObjectDeserializer.class));
- mw.visitFieldInsn(PUTFIELD, context.className, fieldInfo.name + "_asm_deser__", desc(ObjectDeserializer.class));
+ mw.visitFieldInsn(PUTFIELD, context.className, context.fieldDeserName(fieldInfo), desc(ObjectDeserializer.class));
mw.visitLabel(notNull_);
mw.visitVarInsn(ALOAD, 0);
- mw.visitFieldInsn(GETFIELD, context.className, fieldInfo.name + "_asm_deser__", desc(ObjectDeserializer.class));
+ mw.visitFieldInsn(GETFIELD, context.className, context.fieldDeserName(fieldInfo), desc(ObjectDeserializer.class));
}
static class Context {
@@ -1778,13 +1831,52 @@ public int var(String name) {
i = variants.get(name);
return i.intValue();
}
+
+ public int var_asm(FieldInfo fieldInfo) {
+ return var(fieldInfo.name + "_asm");
+ }
+
+ public int var_asm(FieldInfo fieldInfo, int increment) {
+ return var(fieldInfo.name + "_asm", increment);
+ }
+
+ public String fieldName(FieldInfo fieldInfo) {
+ return validIdent(fieldInfo.name)
+ ? fieldInfo.name + "_asm_prefix__"
+ : "asm_field_" + TypeUtils.fnv1a_64_extract(fieldInfo.name);
+ }
+
+
+ public String fieldDeserName(FieldInfo fieldInfo) {
+ return validIdent(fieldInfo.name)
+ ? fieldInfo.name + "_asm_deser__"
+ : "_asm_deser__" + TypeUtils.fnv1a_64_extract(fieldInfo.name);
+ }
+
+
+ boolean validIdent(String name) {
+ for (int i = 0; i < name.length(); ++i) {
+ char ch = name.charAt(i);
+ if (ch == 0) {
+ if (!IOUtils.firstIdentifier(ch)) {
+ return false;
+ }
+ } else {
+ if (!IOUtils.isIdent(ch)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
}
private void _init(ClassWriter cw, Context context) {
for (int i = 0, size = context.fieldInfoList.length; i < size; ++i) {
FieldInfo fieldInfo = context.fieldInfoList[i];
- FieldWriter fw = new FieldWriter(cw, ACC_PUBLIC, fieldInfo.name + "_asm_prefix__", "[C");
+ FieldWriter fw = new FieldWriter(cw, ACC_PUBLIC, context.fieldName(fieldInfo), "[C");
fw.visitEnd();
}
@@ -1801,7 +1893,7 @@ private void _init(ClassWriter cw, Context context) {
desc(ObjectDeserializer.class));
fw.visitEnd();
} else {
- FieldWriter fw = new FieldWriter(cw, ACC_PUBLIC, fieldInfo.name + "_asm_deser__",
+ FieldWriter fw = new FieldWriter(cw, ACC_PUBLIC, context.fieldDeserName(fieldInfo),
desc(ObjectDeserializer.class));
fw.visitEnd();
}
@@ -1822,7 +1914,7 @@ private void _init(ClassWriter cw, Context context) {
mw.visitVarInsn(ALOAD, 0);
mw.visitLdcInsn("\"" + fieldInfo.name + "\":"); // public char[] toCharArray()
mw.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "toCharArray", "()[C");
- mw.visitFieldInsn(PUTFIELD, context.className, fieldInfo.name + "_asm_prefix__", "[C");
+ mw.visitFieldInsn(PUTFIELD, context.className, context.fieldName(fieldInfo), "[C");
}
diff --git a/src/main/java/com/alibaba/fastjson/parser/deserializer/AbstractDateDeserializer.java b/src/main/java/com/alibaba/fastjson/parser/deserializer/AbstractDateDeserializer.java
old mode 100755
new mode 100644
index 9ffb278310..91a4d3020a
--- a/src/main/java/com/alibaba/fastjson/parser/deserializer/AbstractDateDeserializer.java
+++ b/src/main/java/com/alibaba/fastjson/parser/deserializer/AbstractDateDeserializer.java
@@ -3,6 +3,7 @@
import java.lang.reflect.Type;
import java.text.ParseException;
import java.text.SimpleDateFormat;
+import java.util.Locale;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
@@ -22,32 +23,77 @@ public T deserialze(DefaultJSONParser parser, Type clazz, Object fieldName,
Object val;
if (lexer.token() == JSONToken.LITERAL_INT) {
- val = lexer.longValue();
+ long millis = lexer.longValue();
lexer.nextToken(JSONToken.COMMA);
+ if ("unixtime".equals(format)) {
+ millis *= 1000;
+ }
+ val = millis;
} else if (lexer.token() == JSONToken.LITERAL_STRING) {
String strVal = lexer.stringVal();
if (format != null) {
+ if ("yyyy-MM-dd HH:mm:ss.SSSSSSSSS".equals(format)
+ && clazz instanceof Class
+ && ((Class) clazz).getName().equals("java.sql.Timestamp")) {
+ return (T) TypeUtils.castToTimestamp(strVal);
+ }
+
SimpleDateFormat simpleDateFormat = null;
try {
- simpleDateFormat = new SimpleDateFormat(format,JSON.defaultLocale);
+ simpleDateFormat = new SimpleDateFormat(format, parser.lexer.getLocale());
} catch (IllegalArgumentException ex) {
- if (format.equals("yyyy-MM-ddTHH:mm:ss.SSS")) {
- format = "yyyy-MM-dd'T'HH:mm:ss.SSS";
- simpleDateFormat = new SimpleDateFormat(format);
- } else if (format.equals("yyyy-MM-ddTHH:mm:ss")) {
- format = "yyyy-MM-dd'T'HH:mm:ss";
- simpleDateFormat = new SimpleDateFormat(format);
+ if (format.contains("T")) {
+ String fromat2 = format.replaceAll("T", "'T'");
+ try {
+ simpleDateFormat = new SimpleDateFormat(fromat2, parser.lexer.getLocale());
+ } catch (IllegalArgumentException e2) {
+ throw ex;
+ }
}
}
+ if (JSON.defaultTimeZone != null) {
+ simpleDateFormat.setTimeZone(parser.lexer.getTimeZone());
+ }
+
try {
val = simpleDateFormat.parse(strVal);
} catch (ParseException ex) {
+ val = null;
+ // skip
+ }
+
+ if (val == null && JSON.defaultLocale == Locale.CHINA) {
+ try {
+ simpleDateFormat = new SimpleDateFormat(format, Locale.US);
+ } catch (IllegalArgumentException ex) {
+ if (format.contains("T")) {
+ String fromat2 = format.replaceAll("T", "'T'");
+ try {
+ simpleDateFormat = new SimpleDateFormat(fromat2, parser.lexer.getLocale());
+ } catch (IllegalArgumentException e2) {
+ throw ex;
+ }
+ }
+ }
+ simpleDateFormat.setTimeZone(parser.lexer.getTimeZone());
+
+ try {
+ val = simpleDateFormat.parse(strVal);
+ } catch (ParseException ex) {
+ val = null;
+ // skip
+ }
+ }
+
+ if (val == null) {
if (format.equals("yyyy-MM-dd'T'HH:mm:ss.SSS") //
&& strVal.length() == 19) {
try {
- val = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").parse(strVal);
+ SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", JSON.defaultLocale);
+ df.setTimeZone(JSON.defaultTimeZone);
+ val = df.parse(strVal);
} catch (ParseException ex2) {
// skip
val = null;
diff --git a/src/main/java/com/alibaba/fastjson/parser/deserializer/ArrayListTypeFieldDeserializer.java b/src/main/java/com/alibaba/fastjson/parser/deserializer/ArrayListTypeFieldDeserializer.java
old mode 100755
new mode 100644
index c8db519937..158ff8e52f
--- a/src/main/java/com/alibaba/fastjson/parser/deserializer/ArrayListTypeFieldDeserializer.java
+++ b/src/main/java/com/alibaba/fastjson/parser/deserializer/ArrayListTypeFieldDeserializer.java
@@ -8,6 +8,8 @@
import java.util.Collection;
import java.util.Map;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.parser.DefaultJSONParser;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.JSONLexer;
@@ -53,7 +55,11 @@ public void parseField(DefaultJSONParser parser, Object object, Type objectType,
final int token = lexer.token();
if (token == JSONToken.NULL
|| (token == JSONToken.LITERAL_STRING && lexer.stringVal().length() == 0)) {
- setValue(object, null);
+ if (object == null) {
+ fieldValues.put(fieldInfo.name, null);
+ } else {
+ setValue(object, null);
+ }
return;
}
@@ -130,10 +136,27 @@ public final void parseArray(DefaultJSONParser parser, Type objectType, Collecti
if (paramIndex != -1) {
itemActualTypeArgs[0] = paramType.getActualTypeArguments()[paramIndex];
- itemType = new ParameterizedTypeImpl(itemActualTypeArgs, parameterizedItemType.getOwnerType(), parameterizedItemType.getRawType());
+ itemType = TypeReference.intern(
+ new ParameterizedTypeImpl(itemActualTypeArgs, parameterizedItemType.getOwnerType(), parameterizedItemType.getRawType())
+ );
}
}
}
+ } else if (itemType instanceof TypeVariable && objectType instanceof Class) {
+ Class objectClass = (Class) objectType;
+ TypeVariable typeVar = (TypeVariable) itemType;
+ objectClass.getTypeParameters();
+
+ for (int i = 0, size = objectClass.getTypeParameters().length; i < size; ++i) {
+ TypeVariable item = objectClass.getTypeParameters()[i];
+ if (item.getName().equals(typeVar.getName())) {
+ Type[] bounds = item.getBounds();
+ if (bounds.length == 1) {
+ itemType = bounds[0];
+ }
+ break;
+ }
+ }
}
final JSONLexer lexer = parser.lexer;
@@ -147,7 +170,7 @@ public final void parseArray(DefaultJSONParser parser, Type objectType, Collecti
lexer.nextToken(itemFastMatchToken);
- for (int i = 0;; ++i) {
+ for (int i = 0; ; ++i) {
if (lexer.isEnabled(Feature.AllowArbitraryCommas)) {
while (lexer.token() == JSONToken.COMMA) {
lexer.nextToken();
@@ -171,6 +194,11 @@ public final void parseArray(DefaultJSONParser parser, Type objectType, Collecti
}
lexer.nextToken(JSONToken.COMMA);
+ } else if (token == JSONToken.LITERAL_STRING && fieldInfo.unwrapped) {
+ String str = lexer.stringVal();
+ lexer.nextToken();
+ DefaultJSONParser valueParser = new DefaultJSONParser(str);
+ valueParser.parseArray(array);
} else {
if (itemTypeDeser == null) {
itemTypeDeser = deserializer = parser.getConfig().getDeserializer(itemType);
diff --git a/src/main/java/com/alibaba/fastjson/parser/deserializer/AutowiredObjectDeserializer.java b/src/main/java/com/alibaba/fastjson/parser/deserializer/AutowiredObjectDeserializer.java
old mode 100755
new mode 100644
diff --git a/src/main/java/com/alibaba/fastjson/parser/deserializer/DefaultFieldDeserializer.java b/src/main/java/com/alibaba/fastjson/parser/deserializer/DefaultFieldDeserializer.java
old mode 100755
new mode 100644
index 86b2dbcee1..fa578a767a
--- a/src/main/java/com/alibaba/fastjson/parser/deserializer/DefaultFieldDeserializer.java
+++ b/src/main/java/com/alibaba/fastjson/parser/deserializer/DefaultFieldDeserializer.java
@@ -5,7 +5,6 @@
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
-import java.lang.reflect.TypeVariable;
import java.util.Map;
import java.util.zip.GZIPInputStream;
@@ -21,9 +20,15 @@
public class DefaultFieldDeserializer extends FieldDeserializer {
protected ObjectDeserializer fieldValueDeserilizer;
+ protected boolean customDeserilizer = false;
- public DefaultFieldDeserializer(ParserConfig mapping, Class> clazz, FieldInfo fieldInfo){
+ public DefaultFieldDeserializer(ParserConfig config, Class> clazz, FieldInfo fieldInfo){
super(clazz, fieldInfo);
+ JSONField annotation = fieldInfo.getAnnotation();
+ if (annotation != null) {
+ Class> deserializeUsing = annotation.deserializeUsing();
+ customDeserilizer = deserializeUsing != null && deserializeUsing != Void.class;
+ }
}
public ObjectDeserializer getFieldValueDeserilizer(ParserConfig config) {
@@ -59,7 +64,9 @@ public void parseField(DefaultJSONParser parser, Object object, Type objectType,
}
if (fieldType != objectType) {
fieldType = FieldInfo.getFieldType(this.clazz, objectType, fieldType);
- fieldValueDeserilizer = parser.getConfig().getDeserializer(fieldType);
+ if (fieldValueDeserilizer instanceof JavaObjectDeserializer) {
+ fieldValueDeserilizer = parser.getConfig().getDeserializer(fieldType);
+ }
}
}
@@ -69,7 +76,8 @@ public void parseField(DefaultJSONParser parser, Object object, Type objectType,
JavaBeanDeserializer javaBeanDeser = (JavaBeanDeserializer) fieldValueDeserilizer;
value = javaBeanDeser.deserialze(parser, fieldType, fieldInfo.name, fieldInfo.parserFeatures);
} else {
- if (this.fieldInfo.format != null && fieldValueDeserilizer instanceof ContextObjectDeserializer) {
+ if ((this.fieldInfo.format != null || this.fieldInfo.parserFeatures != 0)
+ && fieldValueDeserilizer instanceof ContextObjectDeserializer) {
value = ((ContextObjectDeserializer) fieldValueDeserilizer) //
.deserialze(parser,
fieldType,
diff --git a/src/main/java/com/alibaba/fastjson/parser/deserializer/EnumCreatorDeserializer.java b/src/main/java/com/alibaba/fastjson/parser/deserializer/EnumCreatorDeserializer.java
new file mode 100644
index 0000000000..caebdd72f4
--- /dev/null
+++ b/src/main/java/com/alibaba/fastjson/parser/deserializer/EnumCreatorDeserializer.java
@@ -0,0 +1,33 @@
+package com.alibaba.fastjson.parser.deserializer;
+
+import com.alibaba.fastjson.JSONException;
+import com.alibaba.fastjson.parser.DefaultJSONParser;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+
+public class EnumCreatorDeserializer implements ObjectDeserializer {
+ private final Method creator;
+ private final Class paramType;
+
+ public EnumCreatorDeserializer(Method creator) {
+ this.creator = creator;
+ paramType = creator.getParameterTypes()[0];
+ }
+
+ public T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
+ Object arg = parser.parseObject(paramType);
+ try {
+ return (T) creator.invoke(null, arg);
+ } catch (IllegalAccessException e) {
+ throw new JSONException("parse enum error", e);
+ } catch (InvocationTargetException e) {
+ throw new JSONException("parse enum error", e);
+ }
+ }
+
+ public int getFastMatchToken() {
+ return 0;
+ }
+}
diff --git a/src/main/java/com/alibaba/fastjson/parser/deserializer/EnumDeserializer.java b/src/main/java/com/alibaba/fastjson/parser/deserializer/EnumDeserializer.java
old mode 100755
new mode 100644
index 19bf69e794..3d95a1189d
--- a/src/main/java/com/alibaba/fastjson/parser/deserializer/EnumDeserializer.java
+++ b/src/main/java/com/alibaba/fastjson/parser/deserializer/EnumDeserializer.java
@@ -7,8 +7,13 @@
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.annotation.JSONField;
import com.alibaba.fastjson.parser.DefaultJSONParser;
+import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.JSONLexer;
import com.alibaba.fastjson.parser.JSONToken;
+import com.alibaba.fastjson.util.TypeUtils;
+
+import static com.alibaba.fastjson.util.TypeUtils.fnv1a_64_magic_hashcode;
+import static com.alibaba.fastjson.util.TypeUtils.fnv1a_64_magic_prime;
@SuppressWarnings("rawtypes")
public class EnumDeserializer implements ObjectDeserializer {
@@ -31,7 +36,7 @@ public EnumDeserializer(Class> enumClass){
JSONField jsonField = null;
try {
Field field = enumClass.getField(name);
- jsonField = field.getAnnotation(JSONField.class);
+ jsonField = TypeUtils.getAnnotation(field, JSONField.class);
if (jsonField != null) {
String jsonFieldName = jsonField.name();
if (jsonFieldName != null && jsonFieldName.length() > 0) {
@@ -42,16 +47,16 @@ public EnumDeserializer(Class> enumClass){
// skip
}
- long hash = 0xcbf29ce484222325L;
- long hash_lower = 0xcbf29ce484222325L;
+ long hash = fnv1a_64_magic_hashcode;
+ long hash_lower = fnv1a_64_magic_hashcode;
for (int j = 0; j < name.length(); ++j) {
char ch = name.charAt(j);
hash ^= ch;
hash_lower ^= ((ch >= 'A' && ch <= 'Z') ? (ch + 32) : ch);
- hash *= 0x100000001b3L;
- hash_lower *= 0x100000001b3L;
+ hash *= fnv1a_64_magic_prime;
+ hash_lower *= fnv1a_64_magic_prime;
}
enumMap.put(hash, e);
@@ -61,11 +66,11 @@ public EnumDeserializer(Class> enumClass){
if (jsonField != null) {
for (String alterName : jsonField.alternateNames()) {
- long alterNameHash = 0xcbf29ce484222325L;
+ long alterNameHash = fnv1a_64_magic_hashcode;
for (int j = 0; j < alterName.length(); ++j) {
char ch = alterName.charAt(j);
alterNameHash ^= ch;
- alterNameHash *= 0x100000001b3L;
+ alterNameHash *= fnv1a_64_magic_prime;
}
if (alterNameHash != hash && alterNameHash != hash_lower) {
enumMap.put(alterNameHash, e);
@@ -119,7 +124,7 @@ public T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
int intValue = lexer.intValue();
lexer.nextToken(JSONToken.COMMA);
- if (intValue < 0 || intValue > ordinalEnums.length) {
+ if (intValue < 0 || intValue >= ordinalEnums.length) {
throw new JSONException("parse enum " + enumClass.getName() + " error, value : " + intValue);
}
@@ -132,14 +137,27 @@ public T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
return (T) null;
}
- long hash = 0xcbf29ce484222325L;
+ long hash = fnv1a_64_magic_hashcode;
+ long hash_lower = fnv1a_64_magic_hashcode;
for (int j = 0; j < name.length(); ++j) {
char ch = name.charAt(j);
+
hash ^= ch;
- hash *= 0x100000001b3L;
+ hash_lower ^= ((ch >= 'A' && ch <= 'Z') ? (ch + 32) : ch);
+
+ hash *= fnv1a_64_magic_prime;
+ hash_lower *= fnv1a_64_magic_prime;
}
- return (T) getEnumByHashCode(hash);
+ Enum e = getEnumByHashCode(hash);
+ if (e == null && hash_lower != hash) {
+ e = getEnumByHashCode(hash_lower);
+ }
+
+ if (e == null && lexer.isEnabled(Feature.ErrorOnEnumNotMatch)) {
+ throw new JSONException("not match enum value, " + enumClass.getName() + " : " + name);
+ }
+ return (T) e;
} else if (token == JSONToken.NULL) {
value = null;
lexer.nextToken(JSONToken.COMMA);
diff --git a/src/main/java/com/alibaba/fastjson/parser/deserializer/FieldDeserializer.java b/src/main/java/com/alibaba/fastjson/parser/deserializer/FieldDeserializer.java
old mode 100755
new mode 100644
index 66a903e356..17c78a15b5
--- a/src/main/java/com/alibaba/fastjson/parser/deserializer/FieldDeserializer.java
+++ b/src/main/java/com/alibaba/fastjson/parser/deserializer/FieldDeserializer.java
@@ -1,32 +1,37 @@
package com.alibaba.fastjson.parser.deserializer;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.lang.reflect.Type;
+import com.alibaba.fastjson.JSONException;
+import com.alibaba.fastjson.parser.DefaultJSONParser;
+import com.alibaba.fastjson.parser.ParserConfig;
+import com.alibaba.fastjson.serializer.BeanContext;
+import com.alibaba.fastjson.util.FieldInfo;
+import com.alibaba.fastjson.util.TypeUtils;
+
+import java.lang.reflect.*;
import java.util.Collection;
+import java.util.Collections;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
-import com.alibaba.fastjson.JSONException;
-import com.alibaba.fastjson.parser.DefaultJSONParser;
-import com.alibaba.fastjson.serializer.BeanContext;
-import com.alibaba.fastjson.util.FieldInfo;
-
public abstract class FieldDeserializer {
public final FieldInfo fieldInfo;
- protected final Class> clazz;
-
- protected BeanContext beanContext;
+ protected final Class> clazz;
+
+ protected BeanContext beanContext;
- public FieldDeserializer(Class> clazz, FieldInfo fieldInfo){
+ public FieldDeserializer(Class> clazz, FieldInfo fieldInfo) {
this.clazz = clazz;
this.fieldInfo = fieldInfo;
}
-
+
+ public Class> getOwnerClass() {
+ return clazz;
+ }
+
public abstract void parseField(DefaultJSONParser parser, Object object, Type objectType,
Map fieldValues);
@@ -50,14 +55,14 @@ public void setValue(Object object, String value) {
setValue(object, (Object) value);
}
- @SuppressWarnings({ "rawtypes", "unchecked" })
+ @SuppressWarnings({"rawtypes", "unchecked"})
public void setValue(Object object, Object value) {
if (value == null //
- && fieldInfo.fieldClass.isPrimitive()) {
+ && fieldInfo.fieldClass.isPrimitive()) {
return;
} else if (fieldInfo.fieldClass == String.class
&& fieldInfo.format != null
- && fieldInfo.format.equals("trim")){
+ && fieldInfo.format.equals("trim")) {
value = ((String) value).trim();
}
@@ -69,27 +74,93 @@ public void setValue(Object object, Object value) {
AtomicInteger atomic = (AtomicInteger) method.invoke(object);
if (atomic != null) {
atomic.set(((AtomicInteger) value).get());
+ } else {
+ degradeValueAssignment(fieldInfo.field, method, object, value);
}
} else if (fieldInfo.fieldClass == AtomicLong.class) {
AtomicLong atomic = (AtomicLong) method.invoke(object);
if (atomic != null) {
atomic.set(((AtomicLong) value).get());
+ } else {
+ degradeValueAssignment(fieldInfo.field, method, object, value);
}
} else if (fieldInfo.fieldClass == AtomicBoolean.class) {
AtomicBoolean atomic = (AtomicBoolean) method.invoke(object);
if (atomic != null) {
atomic.set(((AtomicBoolean) value).get());
+ } else {
+ degradeValueAssignment(fieldInfo.field, method, object, value);
}
} else if (Map.class.isAssignableFrom(method.getReturnType())) {
- Map map = (Map) method.invoke(object);
+ Map map = null;
+ try {
+ map = (Map) method.invoke(object);
+ } catch (InvocationTargetException e) {
+ degradeValueAssignment(fieldInfo.field, method, object, value);
+ return;
+ }
if (map != null) {
+ if (map == Collections.emptyMap()) {
+ return;
+ }
+
+ if (map.isEmpty() && ((Map) value).isEmpty()) {
+ return;
+ }
+
+ String mapClassName = map.getClass().getName();
+ if (mapClassName.equals("java.util.ImmutableCollections$Map1")
+ || mapClassName.equals("java.util.ImmutableCollections$MapN")
+ || mapClassName.startsWith("java.util.Collections$Unmodifiable")) {
+ // skip
+
+ return;
+ }
+
+ if (map.getClass().getName().equals("kotlin.collections.EmptyMap")) {
+ degradeValueAssignment(fieldInfo.field, method, object, value);
+ return;
+ }
+
map.putAll((Map) value);
+ } else if (value != null) {
+ degradeValueAssignment(fieldInfo.field, method, object, value);
}
} else {
- Collection collection = (Collection) method.invoke(object);
+ Collection collection = null;
+ try {
+ collection = (Collection) method.invoke(object);
+ } catch (InvocationTargetException e) {
+ degradeValueAssignment(fieldInfo.field, method, object, value);
+ return;
+ }
if (collection != null && value != null) {
- collection.clear();
+ String collectionClassName = collection.getClass().getName();
+
+ if (collection == Collections.emptySet()
+ || collection == Collections.emptyList()
+ || collectionClassName == "java.util.ImmutableCollections$ListN"
+ || collectionClassName == "java.util.ImmutableCollections$List12"
+ || collectionClassName.startsWith("java.util.Collections$Unmodifiable")) {
+ // skip
+ return;
+ }
+
+ if (!collection.isEmpty()) {
+ collection.clear();
+ } else if (((Collection) value).isEmpty()) {
+ return; //skip
+ }
+
+
+ if (collectionClassName.equals("kotlin.collections.EmptyList")
+ || collectionClassName.equals("kotlin.collections.EmptySet")) {
+ degradeValueAssignment(fieldInfo.field, method, object, value);
+ return;
+ }
collection.addAll((Collection) value);
+ } else if (collection == null && value != null) {
+ degradeValueAssignment(fieldInfo.field, method, object, value);
}
}
} else {
@@ -117,11 +188,23 @@ public void setValue(Object object, Object value) {
} else if (Map.class.isAssignableFrom(fieldInfo.fieldClass)) {
Map map = (Map) field.get(object);
if (map != null) {
+ if (map == Collections.emptyMap()
+ || map.getClass().getName().startsWith("java.util.Collections$Unmodifiable")) {
+ // skip
+ return;
+ }
map.putAll((Map) value);
}
} else {
Collection collection = (Collection) field.get(object);
if (collection != null && value != null) {
+ if (collection == Collections.emptySet()
+ || collection == Collections.emptyList()
+ || collection.getClass().getName().startsWith("java.util.Collections$Unmodifiable")) {
+ // skip
+ return;
+ }
+
collection.clear();
collection.addAll((Collection) value);
}
@@ -133,8 +216,43 @@ public void setValue(Object object, Object value) {
}
}
} catch (Exception e) {
- throw new JSONException("set property error, " + fieldInfo.name, e);
+ throw new JSONException("set property error, " + clazz.getName() + "#" + fieldInfo.name, e);
+ }
+ }
+
+ /**
+ * kotlin代理类property的get方法会抛未初始化异常,用set方法直接赋值
+ */
+ private static boolean degradeValueAssignment(
+ Field field,
+ Method getMethod,
+ Object object,
+ Object value
+ ) throws InvocationTargetException, IllegalAccessException {
+ if (setFieldValue(field, object, value)) {
+ return true;
+ }
+
+ try {
+ Method setMethod = object
+ .getClass()
+ .getDeclaredMethod("set" + getMethod.getName().substring(3), getMethod.getReturnType());
+ setMethod.invoke(object, value);
+ return true;
+ } catch (InvocationTargetException ignored) {
+ } catch (NoSuchMethodException ignored) {
+ } catch (IllegalAccessException ignored) {
+ }
+ return false;
+ }
+
+ private static boolean setFieldValue(Field field, Object object, Object value) throws IllegalAccessException {
+ if (field != null
+ && !Modifier.isFinal(field.getModifiers())) {
+ field.set(object, value);
+ return true;
}
+ return false;
}
public void setWrappedValue(String key, Object value) {
diff --git a/src/main/java/com/alibaba/fastjson/parser/deserializer/JavaBeanDeserializer.java b/src/main/java/com/alibaba/fastjson/parser/deserializer/JavaBeanDeserializer.java
old mode 100755
new mode 100644
index b5821dc00e..a19e652a91
--- a/src/main/java/com/alibaba/fastjson/parser/deserializer/JavaBeanDeserializer.java
+++ b/src/main/java/com/alibaba/fastjson/parser/deserializer/JavaBeanDeserializer.java
@@ -1,28 +1,25 @@
package com.alibaba.fastjson.parser.deserializer;
-import java.lang.reflect.*;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.JSONValidator;
import com.alibaba.fastjson.annotation.JSONField;
-import com.alibaba.fastjson.parser.DefaultJSONParser;
+import com.alibaba.fastjson.parser.*;
import com.alibaba.fastjson.parser.DefaultJSONParser.ResolveTask;
-import com.alibaba.fastjson.parser.Feature;
-import com.alibaba.fastjson.parser.JSONLexer;
-import com.alibaba.fastjson.parser.JSONLexerBase;
-import com.alibaba.fastjson.parser.JSONToken;
-import com.alibaba.fastjson.parser.ParseContext;
-import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.util.FieldInfo;
import com.alibaba.fastjson.util.JavaBeanInfo;
import com.alibaba.fastjson.util.TypeUtils;
+import java.lang.reflect.*;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import static com.alibaba.fastjson.util.TypeUtils.fnv1a_64_magic_hashcode;
+
public class JavaBeanDeserializer implements ObjectDeserializer {
private final FieldDeserializer[] fieldDeserializers;
@@ -32,12 +29,15 @@ public class JavaBeanDeserializer implements ObjectDeserializer {
private ConcurrentMap extraFieldDeserializers;
private final Map alterNameFieldDeserializers;
+ private Map fieldDeserializerMap;
private transient long[] smartMatchHashArray;
private transient short[] smartMatchHashArrayMapping;
private transient long[] hashArray;
private transient short[] hashArrayMapping;
+
+ private final ParserConfig.AutoTypeCheckHandler autoTypeCheckHandler;
public JavaBeanDeserializer(ParserConfig config, Class> clazz) {
this(config, clazz, clazz);
@@ -45,7 +45,7 @@ public JavaBeanDeserializer(ParserConfig config, Class> clazz) {
public JavaBeanDeserializer(ParserConfig config, Class> clazz, Type type){
this(config //
- , JavaBeanInfo.build(clazz, type, config.propertyNamingStrategy, config.fieldBased, config.compatibleWithJavaBean)
+ , JavaBeanInfo.build(clazz, type, config.propertyNamingStrategy, config.fieldBased, config.compatibleWithJavaBean, config.isJacksonCompatible())
);
}
@@ -53,6 +53,16 @@ public JavaBeanDeserializer(ParserConfig config, JavaBeanInfo beanInfo){
this.clazz = beanInfo.clazz;
this.beanInfo = beanInfo;
+ ParserConfig.AutoTypeCheckHandler autoTypeCheckHandler = null;
+ if (beanInfo.jsonType != null && beanInfo.jsonType.autoTypeCheckHandler() != ParserConfig.AutoTypeCheckHandler.class) {
+ try {
+ autoTypeCheckHandler = beanInfo.jsonType.autoTypeCheckHandler().newInstance();
+ } catch (Exception e) {
+ //
+ }
+ }
+ this.autoTypeCheckHandler = autoTypeCheckHandler;
+
Map alterNameFieldDeserializers = null;
sortedFieldDeserializers = new FieldDeserializer[beanInfo.sortedFields.length];
for (int i = 0, size = beanInfo.sortedFields.length; i < size; ++i) {
@@ -61,6 +71,13 @@ public JavaBeanDeserializer(ParserConfig config, JavaBeanInfo beanInfo){
sortedFieldDeserializers[i] = fieldDeserializer;
+ if (size > 128) {
+ if (fieldDeserializerMap == null) {
+ fieldDeserializerMap = new HashMap();
+ }
+ fieldDeserializerMap.put(fieldInfo.name, fieldDeserializer);
+ }
+
for (String name : fieldInfo.alternateNames) {
if (alterNameFieldDeserializers == null) {
alterNameFieldDeserializers = new HashMap();
@@ -86,6 +103,13 @@ public FieldDeserializer getFieldDeserializer(String key, int[] setFlags) {
if (key == null) {
return null;
}
+
+ if (fieldDeserializerMap != null) {
+ FieldDeserializer fieldDeserializer = fieldDeserializerMap.get(key);
+ if (fieldDeserializer != null) {
+ return fieldDeserializer;
+ }
+ }
int low = 0;
int high = sortedFieldDeserializers.length - 1;
@@ -159,14 +183,8 @@ static boolean isSetFlag(int i, int[] setFlags) {
}
int flagIndex = i / 32;
- int bitIndex = i % 32;
- if (flagIndex < setFlags.length) {
- if ((setFlags[flagIndex] & (1 << bitIndex)) != 0) {
- return true;
- }
- }
-
- return false;
+ return flagIndex < setFlags.length
+ && (setFlags[flagIndex] & (1 << i % 32)) != 0;
}
public Object createInstance(DefaultJSONParser parser, Type type) {
@@ -230,12 +248,14 @@ public Object createInstance(DefaultJSONParser parser, Type type) {
if (parentName.equals(parentClassName)) {
param = parentContext.object;
}
+ } else {
+ param = ctxObj;
}
} else {
param = ctxObj;
}
- if (param == null) {
+ if (param == null || param instanceof Collection && ((Collection)param).isEmpty()) {
throw new JSONException("can't create non-static inner class instance.");
}
@@ -278,6 +298,22 @@ public T deserialzeArrayMapping(DefaultJSONParser parser, Type type, Object
throw new JSONException("error");
}
+ String typeName = null;
+ if ((typeName = lexer.scanTypeName(parser.symbolTable)) != null) {
+ ObjectDeserializer deserializer = getSeeAlso(parser.getConfig(), this.beanInfo, typeName);
+ Class> userType = null;
+
+ if (deserializer == null) {
+ Class> expectClass = TypeUtils.getClass(type);
+ userType = parser.getConfig().checkAutoType(typeName, expectClass, lexer.getFeatures());
+ deserializer = parser.getConfig().getDeserializer(userType);
+ }
+
+ if (deserializer instanceof JavaBeanDeserializer) {
+ return ((JavaBeanDeserializer) deserializer).deserialzeArrayMapping(parser, type, fieldName, object);
+ }
+ }
+
object = createInstance(parser, type);
for (int i = 0, size = sortedFieldDeserializers.length; i < size; ++i) {
@@ -424,8 +460,6 @@ protected T deserialze(DefaultJSONParser parser, //
}
}
}
- } else if (token == JSONToken.LITERAL_ISO8601_DATE) {
- Calendar calendar = lexer.getCalendar();
}
if (token == JSONToken.LBRACKET && lexer.getCurrent() == ']') {
@@ -433,8 +467,29 @@ protected T deserialze(DefaultJSONParser parser, //
lexer.nextToken();
return null;
}
+
+ if (beanInfo.factoryMethod != null && beanInfo.fields.length == 1) {
+ try {
+ FieldInfo field = beanInfo.fields[0];
+ if (field.fieldClass == Integer.class) {
+ if (token == JSONToken.LITERAL_INT) {
+ int intValue = lexer.intValue();
+ lexer.nextToken();
+ return (T) createFactoryInstance(config, intValue);
+ }
+ } else if (field.fieldClass == String.class) {
+ if (token == JSONToken.LITERAL_STRING) {
+ String stringVal = lexer.stringVal();
+ lexer.nextToken();
+ return (T) createFactoryInstance(config, stringVal);
+ }
+ }
+ } catch (Exception ex) {
+ throw new JSONException(ex.getMessage(), ex);
+ }
+ }
- StringBuffer buf = (new StringBuffer()) //
+ StringBuilder buf = (new StringBuilder()) //
.append("syntax error, expect {, actual ") //
.append(lexer.tokenName()) //
.append(", pos ") //
@@ -456,42 +511,60 @@ protected T deserialze(DefaultJSONParser parser, //
}
String typeKey = beanInfo.typeKey;
- for (int fieldIndex = 0;; fieldIndex++) {
+ for (int fieldIndex = 0, notMatchCount = 0;; fieldIndex++) {
String key = null;
- FieldDeserializer fieldDeser = null;
+ FieldDeserializer fieldDeserializer = null;
FieldInfo fieldInfo = null;
Class> fieldClass = null;
- JSONField feildAnnotation = null;
- if (fieldIndex < sortedFieldDeserializers.length) {
- fieldDeser = sortedFieldDeserializers[fieldIndex];
- fieldInfo = fieldDeser.fieldInfo;
+ JSONField fieldAnnotation = null;
+ boolean customDeserializer = false;
+ if (fieldIndex < sortedFieldDeserializers.length && notMatchCount < 16) {
+ fieldDeserializer = sortedFieldDeserializers[fieldIndex];
+ fieldInfo = fieldDeserializer.fieldInfo;
fieldClass = fieldInfo.fieldClass;
- feildAnnotation = fieldInfo.getAnnotation();
+ fieldAnnotation = fieldInfo.getAnnotation();
+ if (fieldAnnotation != null && fieldDeserializer instanceof DefaultFieldDeserializer) {
+ customDeserializer = ((DefaultFieldDeserializer) fieldDeserializer).customDeserilizer;
+ }
}
boolean matchField = false;
boolean valueParsed = false;
Object fieldValue = null;
- if (fieldDeser != null) {
+ if (fieldDeserializer != null) {
char[] name_chars = fieldInfo.name_chars;
- if (fieldClass == int.class || fieldClass == Integer.class) {
- fieldValue = lexer.scanFieldInt(name_chars);
-
+ if (customDeserializer && lexer.matchField(name_chars)) {
+ matchField = true;
+ } else if (fieldClass == int.class || fieldClass == Integer.class) {
+ int intVal = lexer.scanFieldInt(name_chars);
+ if (intVal == 0 && lexer.matchStat == JSONLexer.VALUE_NULL) {
+ fieldValue = null;
+ } else {
+ fieldValue = intVal;
+ }
+
if (lexer.matchStat > 0) {
matchField = true;
valueParsed = true;
} else if (lexer.matchStat == JSONLexer.NOT_MATCH_NAME) {
- continue;
+ notMatchCount++;
+ continue;
}
} else if (fieldClass == long.class || fieldClass == Long.class) {
- fieldValue = lexer.scanFieldLong(name_chars);
-
+ long longVal = lexer.scanFieldLong(name_chars);
+ if (longVal == 0 && lexer.matchStat == JSONLexer.VALUE_NULL) {
+ fieldValue = null;
+ } else {
+ fieldValue = longVal;
+ }
+
if (lexer.matchStat > 0) {
matchField = true;
valueParsed = true;
} else if (lexer.matchStat == JSONLexer.NOT_MATCH_NAME) {
- continue;
+ notMatchCount++;
+ continue;
}
} else if (fieldClass == String.class) {
fieldValue = lexer.scanFieldString(name_chars);
@@ -500,7 +573,8 @@ protected T deserialze(DefaultJSONParser parser, //
matchField = true;
valueParsed = true;
} else if (lexer.matchStat == JSONLexer.NOT_MATCH_NAME) {
- continue;
+ notMatchCount++;
+ continue;
}
} else if (fieldClass == java.util.Date.class && fieldInfo.format == null) {
fieldValue = lexer.scanFieldDate(name_chars);
@@ -509,6 +583,7 @@ protected T deserialze(DefaultJSONParser parser, //
matchField = true;
valueParsed = true;
} else if (lexer.matchStat == JSONLexer.NOT_MATCH_NAME) {
+ notMatchCount++;
continue;
}
} else if (fieldClass == BigDecimal.class) {
@@ -518,6 +593,7 @@ protected T deserialze(DefaultJSONParser parser, //
matchField = true;
valueParsed = true;
} else if (lexer.matchStat == JSONLexer.NOT_MATCH_NAME) {
+ notMatchCount++;
continue;
}
} else if (fieldClass == BigInteger.class) {
@@ -527,47 +603,68 @@ protected T deserialze(DefaultJSONParser parser, //
matchField = true;
valueParsed = true;
} else if (lexer.matchStat == JSONLexer.NOT_MATCH_NAME) {
+ notMatchCount++;
continue;
}
} else if (fieldClass == boolean.class || fieldClass == Boolean.class) {
- fieldValue = lexer.scanFieldBoolean(name_chars);
+ boolean booleanVal = lexer.scanFieldBoolean(name_chars);
+
+ if (lexer.matchStat == JSONLexer.VALUE_NULL) {
+ fieldValue = null;
+ } else {
+ fieldValue = booleanVal;
+ }
if (lexer.matchStat > 0) {
matchField = true;
valueParsed = true;
} else if (lexer.matchStat == JSONLexer.NOT_MATCH_NAME) {
- continue;
+ notMatchCount++;
+ continue;
}
} else if (fieldClass == float.class || fieldClass == Float.class) {
- fieldValue = lexer.scanFieldFloat(name_chars);
-
+ float floatVal = lexer.scanFieldFloat(name_chars);
+ if (floatVal == 0 && lexer.matchStat == JSONLexer.VALUE_NULL) {
+ fieldValue = null;
+ } else {
+ fieldValue = floatVal;
+ }
+
if (lexer.matchStat > 0) {
matchField = true;
valueParsed = true;
} else if (lexer.matchStat == JSONLexer.NOT_MATCH_NAME) {
- continue;
+ notMatchCount++;
+ continue;
}
} else if (fieldClass == double.class || fieldClass == Double.class) {
- fieldValue = lexer.scanFieldDouble(name_chars);
-
+ double doubleVal = lexer.scanFieldDouble(name_chars);
+ if (doubleVal == 0 && lexer.matchStat == JSONLexer.VALUE_NULL) {
+ fieldValue = null;
+ } else {
+ fieldValue = doubleVal;
+ }
+
if (lexer.matchStat > 0) {
matchField = true;
valueParsed = true;
} else if (lexer.matchStat == JSONLexer.NOT_MATCH_NAME) {
- continue;
+ notMatchCount++;
+ continue;
}
} else if (fieldClass.isEnum() //
&& parser.getConfig().getDeserializer(fieldClass) instanceof EnumDeserializer
- && (feildAnnotation == null || feildAnnotation.deserializeUsing() == Void.class)
+ && (fieldAnnotation == null || fieldAnnotation.deserializeUsing() == Void.class)
) {
- if (fieldDeser instanceof DefaultFieldDeserializer) {
- ObjectDeserializer fieldValueDeserilizer = ((DefaultFieldDeserializer) fieldDeser).fieldValueDeserilizer;
+ if (fieldDeserializer instanceof DefaultFieldDeserializer) {
+ ObjectDeserializer fieldValueDeserilizer = ((DefaultFieldDeserializer) fieldDeserializer).fieldValueDeserilizer;
fieldValue = this.scanEnum(lexer, name_chars, fieldValueDeserilizer);
if (lexer.matchStat > 0) {
matchField = true;
valueParsed = true;
} else if (lexer.matchStat == JSONLexer.NOT_MATCH_NAME) {
+ notMatchCount++;
continue;
}
}
@@ -578,6 +675,7 @@ protected T deserialze(DefaultJSONParser parser, //
matchField = true;
valueParsed = true;
} else if (lexer.matchStat == JSONLexer.NOT_MATCH_NAME) {
+ notMatchCount++;
continue;
}
} else if (fieldClass == float[].class) {
@@ -587,6 +685,7 @@ protected T deserialze(DefaultJSONParser parser, //
matchField = true;
valueParsed = true;
} else if (lexer.matchStat == JSONLexer.NOT_MATCH_NAME) {
+ notMatchCount++;
continue;
}
} else if (fieldClass == float[][].class) {
@@ -596,6 +695,7 @@ protected T deserialze(DefaultJSONParser parser, //
matchField = true;
valueParsed = true;
} else if (lexer.matchStat == JSONLexer.NOT_MATCH_NAME) {
+ notMatchCount++;
continue;
}
} else if (lexer.matchField(name_chars)) {
@@ -649,6 +749,17 @@ protected T deserialze(DefaultJSONParser parser, //
parser.resolveStatus = DefaultJSONParser.NeedToResolve;
}
} else {
+ if (ref.indexOf('\\') > 0) {
+ StringBuilder buf = new StringBuilder();
+ for (int i = 0; i < ref.length(); ++i) {
+ char ch = ref.charAt(i);
+ if (ch == '\\') {
+ ch = ref.charAt(++i);
+ }
+ buf.append(ch);
+ }
+ ref = buf.toString();
+ }
Object refObj = parser.resolveReference(ref);
if (refObj != null) {
object = refObj;
@@ -693,7 +804,24 @@ protected T deserialze(DefaultJSONParser parser, //
if (deserializer == null) {
Class> expectClass = TypeUtils.getClass(type);
- userType = config.checkAutoType(typeName, expectClass, lexer.getFeatures());
+
+ if (autoTypeCheckHandler != null) {
+ userType = autoTypeCheckHandler.handler(typeName, expectClass, lexer.getFeatures());
+ }
+
+ if (userType == null) {
+ if (typeName.equals("java.util.HashMap") || typeName.equals("java.util.LinkedHashMap")) {
+ if (lexer.token() == JSONToken.RBRACE) {
+ lexer.nextToken();
+ break;
+ }
+ continue;
+ }
+ }
+
+ if (userType == null) {
+ userType = config.checkAutoType(typeName, expectClass, lexer.getFeatures());
+ }
deserializer = parser.getConfig().getDeserializer(userType);
}
@@ -702,7 +830,9 @@ protected T deserialze(DefaultJSONParser parser, //
JavaBeanDeserializer javaBeanDeserializer = (JavaBeanDeserializer) deserializer;
if (typeKey != null) {
FieldDeserializer typeKeyFieldDeser = javaBeanDeserializer.getFieldDeserializer(typeKey);
- typeKeyFieldDeser.setValue(typedObject, typeName);
+ if (typeKeyFieldDeser != null) {
+ typeKeyFieldDeser.setValue(typedObject, typeName);
+ }
}
}
return (T) typedObject;
@@ -725,7 +855,7 @@ protected T deserialze(DefaultJSONParser parser, //
if (matchField) {
if (!valueParsed) {
- fieldDeser.parseField(parser, object, type, fieldValues);
+ fieldDeserializer.parseField(parser, object, type, fieldValues);
} else {
if (object == null) {
fieldValues.put(fieldInfo.name, fieldValue);
@@ -736,16 +866,23 @@ protected T deserialze(DefaultJSONParser parser, //
&& fieldClass != double.class //
&& fieldClass != boolean.class //
) {
- fieldDeser.setValue(object, fieldValue);
+ fieldDeserializer.setValue(object, fieldValue);
}
} else {
- fieldDeser.setValue(object, fieldValue);
+ if (fieldClass == String.class
+ && ((features & Feature.TrimStringFieldValue.mask) != 0
+ || (beanInfo.parserFeatures & Feature.TrimStringFieldValue.mask) != 0
+ || (fieldInfo.parserFeatures & Feature.TrimStringFieldValue.mask) != 0)) {
+ fieldValue = ((String) fieldValue).trim();
+ }
+
+ fieldDeserializer.setValue(object, fieldValue);
}
if (setFlags != null) {
int flagIndex = fieldIndex / 32;
int bitIndex = fieldIndex % 32;
- setFlags[flagIndex] |= (1 >> bitIndex);
+ setFlags[flagIndex] |= (1 << bitIndex);
}
if (lexer.matchStat == JSONLexer.END) {
@@ -753,7 +890,9 @@ protected T deserialze(DefaultJSONParser parser, //
}
}
} else {
- boolean match = parseField(parser, key, object, type, fieldValues, setFlags);
+ boolean match = parseField(parser, key, object, type,
+ fieldValues == null ? new HashMap(this.fieldDeserializers.length) : fieldValues, setFlags);
+
if (!match) {
if (lexer.token() == JSONToken.RBRACE) {
lexer.nextToken();
@@ -818,6 +957,24 @@ protected T deserialze(DefaultJSONParser parser, //
&& (fieldInfo.parserFeatures & Feature.InitStringFieldAsEmpty.mask) != 0) {
param = "";
}
+ } else {
+ if (beanInfo.creatorConstructorParameterTypes != null && i < beanInfo.creatorConstructorParameterTypes.length) {
+ Type paramType = beanInfo.creatorConstructorParameterTypes[i];
+ if (paramType instanceof Class) {
+ Class paramClass = (Class) paramType;
+ if (!paramClass.isInstance(param)) {
+ if (param instanceof List) {
+ List list = (List) param;
+ if (list.size() == 1) {
+ Object first = list.get(0);
+ if (paramClass.isInstance(first)) {
+ param = list.get(0);
+ }
+ }
+ }
+ }
+ }
+ }
}
params[i] = param;
}
@@ -854,8 +1011,33 @@ protected T deserialze(DefaultJSONParser parser, //
}
if (beanInfo.creatorConstructor != null) {
+ boolean hasNull = false;
+ if (beanInfo.kotlin) {
+ for (int i = 0; i < params.length; i++) {
+ if (params[i] == null && beanInfo.fields != null && i < beanInfo.fields.length) {
+ FieldInfo fieldInfo = beanInfo.fields[i];
+ if (fieldInfo.fieldClass == String.class) {
+ hasNull = true;
+ }
+ break;
+ }
+ }
+ }
+
try {
- object = beanInfo.creatorConstructor.newInstance(params);
+ if (hasNull && beanInfo.kotlinDefaultConstructor != null) {
+ object = beanInfo.kotlinDefaultConstructor.newInstance(new Object[0]);
+
+ for (int i = 0; i < params.length; i++) {
+ final Object param = params[i];
+ if (param != null && beanInfo.fields != null && i < beanInfo.fields.length) {
+ FieldInfo fieldInfo = beanInfo.fields[i];
+ fieldInfo.set(object, param);
+ }
+ }
+ } else {
+ object = beanInfo.creatorConstructor.newInstance(params);
+ }
} catch (Exception e) {
throw new JSONException("create instance error, " + paramNames + ", "
+ beanInfo.creatorConstructor.toGenericString(), e);
@@ -877,7 +1059,9 @@ protected T deserialze(DefaultJSONParser parser, //
}
}
- childContext.object = object;
+ if (childContext != null) {
+ childContext.object = object;
+ }
}
Method buildMethod = beanInfo.buildMethod;
@@ -913,9 +1097,20 @@ protected Enum scanEnum(JSONLexerBase lexer, char[] name_chars, ObjectDeserializ
return null;
}
- long enumNameHashCode = lexer.scanFieldSymbol(name_chars);
+ long enumNameHashCode = lexer.scanEnumSymbol(name_chars);
if (lexer.matchStat > 0) {
- return enumDeserializer.getEnumByHashCode(enumNameHashCode);
+ Enum e = enumDeserializer.getEnumByHashCode(enumNameHashCode);
+ if (e == null) {
+ if (enumNameHashCode == fnv1a_64_magic_hashcode) {
+ return null;
+ }
+
+ if (lexer.isEnabled(Feature.ErrorOnEnumNotMatch)) {
+ throw new JSONException("not match enum value, " + enumDeserializer.enumClass);
+ }
+ }
+
+ return e;
} else {
return null;
}
@@ -931,9 +1126,12 @@ public boolean parseField(DefaultJSONParser parser, String key, Object object, T
JSONLexer lexer = parser.lexer; // xxx
final int disableFieldSmartMatchMask = Feature.DisableFieldSmartMatch.mask;
+ final int initStringFieldAsEmpty = Feature.InitStringFieldAsEmpty.mask;
FieldDeserializer fieldDeserializer;
if (lexer.isEnabled(disableFieldSmartMatchMask) || (this.beanInfo.parserFeatures & disableFieldSmartMatchMask) != 0) {
fieldDeserializer = getFieldDeserializer(key);
+ } else if (lexer.isEnabled(initStringFieldAsEmpty) || (this.beanInfo.parserFeatures & initStringFieldAsEmpty) != 0) {
+ fieldDeserializer = smartMatch(key);
} else {
fieldDeserializer = smartMatch(key, setFlags);
}
@@ -955,6 +1153,13 @@ public boolean parseField(DefaultJSONParser parser, String key, Object object, T
if ((fieldModifiers & Modifier.FINAL) != 0 || (fieldModifiers & Modifier.STATIC) != 0) {
continue;
}
+ JSONField jsonField = TypeUtils.getAnnotation(field, JSONField.class);
+ if (jsonField != null) {
+ String alteredFieldName = jsonField.name();
+ if (!"".equals(alteredFieldName)) {
+ fieldName = alteredFieldName;
+ }
+ }
extraFieldDeserializers.put(fieldName, field);
}
}
@@ -980,7 +1185,10 @@ public boolean parseField(DefaultJSONParser parser, String key, Object object, T
throw new JSONException("setter not found, class " + clazz.getName() + ", property " + key);
}
- for (FieldDeserializer fieldDeser : this.sortedFieldDeserializers) {
+ int fieldIndex = -1;
+ for (int i = 0; i < this.sortedFieldDeserializers.length; i++) {
+ FieldDeserializer fieldDeser = this.sortedFieldDeserializers[i];
+
FieldInfo fieldInfo = fieldDeser.fieldInfo;
if (fieldInfo.unwrapped //
&& fieldDeser instanceof DefaultFieldDeserializer) {
@@ -1000,7 +1208,7 @@ public boolean parseField(DefaultJSONParser parser, String key, Object object, T
}
lexer.nextTokenWithColon(defaultFieldDeserializer.getFastMatchToken());
unwrappedFieldDeser.parseField(parser, fieldObject, objectType, fieldValues);
- return true;
+ fieldIndex = i;
} catch (Exception e) {
throw new JSONException("parse unwrapped field error.", e);
}
@@ -1022,7 +1230,7 @@ public boolean parseField(DefaultJSONParser parser, String key, Object object, T
} catch (Exception e) {
throw new JSONException("parse unwrapped field error.", e);
}
- return true;
+ fieldIndex = i;
}
} else if (fieldInfo.method.getParameterTypes().length == 2) {
lexer.nextTokenWithColon();
@@ -1032,10 +1240,19 @@ public boolean parseField(DefaultJSONParser parser, String key, Object object, T
} catch (Exception e) {
throw new JSONException("parse unwrapped field error.", e);
}
- return true;
+ fieldIndex = i;
}
}
}
+
+ if (fieldIndex != -1) {
+ if (setFlags != null) {
+ int flagIndex = fieldIndex / 32;
+ int bitIndex = fieldIndex % 32;
+ setFlags[flagIndex] |= (1 << bitIndex);
+ }
+ return true;
+ }
parser.parseExtra(object, key);
@@ -1060,6 +1277,12 @@ public boolean parseField(DefaultJSONParser parser, String key, Object object, T
fieldDeserializer.parseField(parser, object, objectType, fieldValues);
+ if (setFlags != null) {
+ int flagIndex = fieldIndex / 32;
+ int bitIndex = fieldIndex % 32;
+ setFlags[flagIndex] |= (1 << bitIndex);
+ }
+
return true;
}
@@ -1075,21 +1298,26 @@ public FieldDeserializer smartMatch(String key, int[] setFlags) {
FieldDeserializer fieldDeserializer = getFieldDeserializer(key, setFlags);
if (fieldDeserializer == null) {
- long smartKeyHash = TypeUtils.fnv1a_64_lower(key);
if (this.smartMatchHashArray == null) {
long[] hashArray = new long[sortedFieldDeserializers.length];
for (int i = 0; i < sortedFieldDeserializers.length; i++) {
- hashArray[i] = TypeUtils.fnv1a_64_lower(sortedFieldDeserializers[i].fieldInfo.name);
+ hashArray[i] = sortedFieldDeserializers[i].fieldInfo.nameHashCode;
}
Arrays.sort(hashArray);
this.smartMatchHashArray = hashArray;
}
// smartMatchHashArrayMapping
+ long smartKeyHash = TypeUtils.fnv1a_64_lower(key);
int pos = Arrays.binarySearch(smartMatchHashArray, smartKeyHash);
+ if (pos < 0) {
+ long smartKeyHash1 = TypeUtils.fnv1a_64_extract(key);
+ pos = Arrays.binarySearch(smartMatchHashArray, smartKeyHash1);
+ }
+
boolean is = false;
if (pos < 0 && (is = key.startsWith("is"))) {
- smartKeyHash = TypeUtils.fnv1a_64_lower(key.substring(2));
+ smartKeyHash = TypeUtils.fnv1a_64_extract(key.substring(2));
pos = Arrays.binarySearch(smartMatchHashArray, smartKeyHash);
}
@@ -1098,8 +1326,7 @@ public FieldDeserializer smartMatch(String key, int[] setFlags) {
short[] mapping = new short[smartMatchHashArray.length];
Arrays.fill(mapping, (short) -1);
for (int i = 0; i < sortedFieldDeserializers.length; i++) {
- int p = Arrays.binarySearch(smartMatchHashArray
- , TypeUtils.fnv1a_64_lower(sortedFieldDeserializers[i].fieldInfo.name));
+ int p = Arrays.binarySearch(smartMatchHashArray, sortedFieldDeserializers[i].fieldInfo.nameHashCode);
if (p >= 0) {
mapping[p] = (short) i;
}
@@ -1135,6 +1362,13 @@ public FieldDeserializer smartMatch(String key, int[] setFlags) {
public int getFastMatchToken() {
return JSONToken.LBRACE;
}
+
+ private Object createFactoryInstance(ParserConfig config, Object value) //
+ throws IllegalArgumentException,
+ IllegalAccessException,
+ InvocationTargetException {
+ return beanInfo.factoryMethod.invoke(null, value);
+ }
public Object createInstance(Map map, ParserConfig config) //
throws IllegalArgumentException,
@@ -1155,8 +1389,103 @@ public Object createInstance(Map map, ParserConfig config) //
}
final FieldInfo fieldInfo = fieldDeser.fieldInfo;
+ Field field = fieldDeser.fieldInfo.field;
Type paramType = fieldInfo.fieldType;
- value = TypeUtils.cast(value, paramType, config);
+
+ Class> fieldClass = fieldInfo.fieldClass;
+ JSONField fieldAnnation = fieldInfo.getAnnotation();
+
+ if (fieldInfo.declaringClass != null
+ && ((!fieldClass.isInstance(value))
+ || (fieldAnnation != null && fieldAnnation.deserializeUsing() != Void.class))
+ ) {
+ String input;
+ if (value instanceof String
+ && JSONValidator.from(((String) value))
+ .validate())
+ {
+ input = (String) value;
+ } else {
+ input = JSON.toJSONString(value);
+ }
+
+ DefaultJSONParser parser = new DefaultJSONParser(input);
+ fieldDeser.parseField(parser, object, paramType, null);
+ continue;
+ }
+
+ if (field != null && fieldInfo.method == null) {
+ Class fieldType = field.getType();
+ if (fieldType == boolean.class) {
+ if (value == Boolean.FALSE) {
+ field.setBoolean(object, false);
+ continue;
+ }
+
+ if (value == Boolean.TRUE) {
+ field.setBoolean(object, true);
+ continue;
+ }
+ } else if (fieldType == int.class) {
+ if (value instanceof Number) {
+ field.setInt(object, ((Number) value).intValue());
+ continue;
+ }
+ } else if (fieldType == long.class) {
+ if (value instanceof Number) {
+ field.setLong(object, ((Number) value).longValue());
+ continue;
+ }
+ } else if (fieldType == float.class) {
+ if (value instanceof Number) {
+ field.setFloat(object, ((Number) value).floatValue());
+ continue;
+ } else if (value instanceof String) {
+ String strVal = (String) value;
+ float floatValue;
+ if (strVal.length() <= 10) {
+ floatValue = TypeUtils.parseFloat(strVal);
+ } else {
+ floatValue = Float.parseFloat(strVal);
+ }
+
+ field.setFloat(object, floatValue);
+ continue;
+ }
+ } else if (fieldType == double.class) {
+ if (value instanceof Number) {
+ field.setDouble(object, ((Number) value).doubleValue());
+ continue;
+ } else if (value instanceof String) {
+ String strVal = (String) value;
+ double doubleValue;
+ if (strVal.length() <= 10) {
+ doubleValue = TypeUtils.parseDouble(strVal);
+ } else {
+ doubleValue = Double.parseDouble(strVal);
+ }
+
+ field.setDouble(object, doubleValue);
+ continue;
+ }
+ } else if (value != null && paramType == value.getClass()) {
+ field.set(object, value);
+ continue;
+ }
+ }
+
+ String format = fieldInfo.format;
+ if (format != null && paramType == Date.class) {
+ value = TypeUtils.castToDate(value, format);
+ } else if (format != null && (paramType instanceof Class) && (((Class) paramType).getName().equals("java.time.LocalDateTime"))) {
+ value = Jdk8DateCodec.castToLocalDateTime(value, format);
+ } else {
+ if (paramType instanceof ParameterizedType) {
+ value = TypeUtils.cast(value, (ParameterizedType) paramType, config);
+ } else {
+ value = TypeUtils.cast(value, paramType, config);
+ }
+ }
fieldDeser.setValue(object, value);
}
@@ -1179,9 +1508,11 @@ public Object createInstance(Map map, ParserConfig config) //
FieldInfo[] fieldInfoList = beanInfo.fields;
int size = fieldInfoList.length;
Object[] params = new Object[size];
+ Map missFields = null;
for (int i = 0; i < size; ++i) {
FieldInfo fieldInfo = fieldInfoList[i];
Object param = map.get(fieldInfo.name);
+
if (param == null) {
Class> fieldClass = fieldInfo.fieldClass;
if (fieldClass == int.class) {
@@ -1201,16 +1532,69 @@ public Object createInstance(Map map, ParserConfig config) //
} else if (fieldClass == boolean.class) {
param = false;
}
+ if (missFields == null) {
+ missFields = new HashMap();
+ }
+ missFields.put(fieldInfo.name, i);
}
params[i] = param;
}
-
+
+ if (missFields != null) {
+ for (Map.Entry entry : map.entrySet()) {
+ String key = entry.getKey();
+ Object value = entry.getValue();
+
+ FieldDeserializer fieldDeser = smartMatch(key);
+ if (fieldDeser != null) {
+ Integer index = missFields.get(fieldDeser.fieldInfo.name);
+ if (index != null) {
+ params[index] = value;
+ }
+ }
+ }
+ }
+
if (beanInfo.creatorConstructor != null) {
- try {
- object = beanInfo.creatorConstructor.newInstance(params);
- } catch (Exception e) {
- throw new JSONException("create instance error, "
- + beanInfo.creatorConstructor.toGenericString(), e);
+ boolean hasNull = false;
+ if (beanInfo.kotlin) {
+ for (int i = 0; i < params.length; i++) {
+ Object param = params[i];
+ if (param == null) {
+ if (beanInfo.fields != null && i < beanInfo.fields.length) {
+ FieldInfo fieldInfo = beanInfo.fields[i];
+ if (fieldInfo.fieldClass == String.class) {
+ hasNull = true;
+ }
+ }
+ } else if (param.getClass() != beanInfo.fields[i].fieldClass){
+ params[i] = TypeUtils.cast(param, beanInfo.fields[i].fieldClass, config);
+ }
+ }
+ }
+
+ if (hasNull && beanInfo.kotlinDefaultConstructor != null) {
+ try {
+ object = beanInfo.kotlinDefaultConstructor.newInstance();
+
+ for (int i = 0; i < params.length; i++) {
+ final Object param = params[i];
+ if (param != null && beanInfo.fields != null && i < beanInfo.fields.length) {
+ FieldInfo fieldInfo = beanInfo.fields[i];
+ fieldInfo.set(object, param);
+ }
+ }
+ } catch (Exception e) {
+ throw new JSONException("create instance error, "
+ + beanInfo.creatorConstructor.toGenericString(), e);
+ }
+ } else {
+ try {
+ object = beanInfo.creatorConstructor.newInstance(params);
+ } catch (Exception e) {
+ throw new JSONException("create instance error, "
+ + beanInfo.creatorConstructor.toGenericString(), e);
+ }
}
} else if (beanInfo.factoryMethod != null) {
try {
@@ -1242,7 +1626,7 @@ protected Object parseRest(DefaultJSONParser parser
return value;
}
- protected JavaBeanDeserializer getSeeAlso(ParserConfig config, JavaBeanInfo beanInfo, String typeName) {
+ protected static JavaBeanDeserializer getSeeAlso(ParserConfig config, JavaBeanInfo beanInfo, String typeName) {
if (beanInfo.jsonType == null) {
return null;
}
diff --git a/src/main/java/com/alibaba/fastjson/parser/deserializer/JavaObjectDeserializer.java b/src/main/java/com/alibaba/fastjson/parser/deserializer/JavaObjectDeserializer.java
index fbca60aea1..e511eb3c4d 100644
--- a/src/main/java/com/alibaba/fastjson/parser/deserializer/JavaObjectDeserializer.java
+++ b/src/main/java/com/alibaba/fastjson/parser/deserializer/JavaObjectDeserializer.java
@@ -1,5 +1,6 @@
package com.alibaba.fastjson.parser.deserializer;
+import java.io.Closeable;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
@@ -10,6 +11,7 @@
import com.alibaba.fastjson.parser.DefaultJSONParser;
import com.alibaba.fastjson.parser.JSONToken;
+import com.alibaba.fastjson.util.TypeUtils;
public class JavaObjectDeserializer implements ObjectDeserializer {
@@ -26,19 +28,18 @@ public T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
List list = new ArrayList();
parser.parseArray(componentType, list);
- Class> componentClass;
- if (componentType instanceof Class) {
- componentClass = (Class>) componentType;
- Object[] array = (Object[]) Array.newInstance(componentClass, list.size());
- list.toArray(array);
- return (T) array;
- } else {
- return (T) list.toArray();
- }
-
+ Class> componentClass = TypeUtils.getRawClass(componentType);
+ Object[] array = (Object[]) Array.newInstance(componentClass, list.size());
+ list.toArray(array);
+ return (T) array;
}
- if (type instanceof Class && type != Object.class && type != Serializable.class) {
+ if (type instanceof Class
+ && type != Object.class
+ && type != Serializable.class
+ && type != Cloneable.class
+ && type != Closeable.class
+ && type != Comparable.class) {
return (T) parser.parseObject(type);
}
diff --git a/src/main/java/com/alibaba/fastjson/parser/deserializer/Jdk8DateCodec.java b/src/main/java/com/alibaba/fastjson/parser/deserializer/Jdk8DateCodec.java
index 2c2f698575..b6644343a0 100644
--- a/src/main/java/com/alibaba/fastjson/parser/deserializer/Jdk8DateCodec.java
+++ b/src/main/java/com/alibaba/fastjson/parser/deserializer/Jdk8DateCodec.java
@@ -12,44 +12,53 @@
import java.time.Period;
import java.time.ZoneId;
import java.time.ZonedDateTime;
+import java.time.chrono.ChronoZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
+import java.util.Date;
import java.util.Locale;
+import java.util.TimeZone;
import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.DefaultJSONParser;
import com.alibaba.fastjson.parser.JSONLexer;
+import com.alibaba.fastjson.parser.JSONScanner;
import com.alibaba.fastjson.parser.JSONToken;
import com.alibaba.fastjson.serializer.*;
+import com.alibaba.fastjson.util.TypeUtils;
public class Jdk8DateCodec extends ContextObjectDeserializer implements ObjectSerializer, ContextObjectSerializer, ObjectDeserializer {
- public static final Jdk8DateCodec instance = new Jdk8DateCodec();
-
- private final static String defaultPatttern = "yyyy-MM-dd HH:mm:ss";
- private final static DateTimeFormatter defaultFormatter = DateTimeFormatter.ofPattern(defaultPatttern);
- private final static DateTimeFormatter formatter_dt19_tw = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
- private final static DateTimeFormatter formatter_dt19_cn = DateTimeFormatter.ofPattern("yyyy年M月d日 HH:mm:ss");
- private final static DateTimeFormatter formatter_dt19_cn_1 = DateTimeFormatter.ofPattern("yyyy年M月d日 H时m分s秒");
- private final static DateTimeFormatter formatter_dt19_kr = DateTimeFormatter.ofPattern("yyyy년M월d일 HH:mm:ss");
- private final static DateTimeFormatter formatter_dt19_us = DateTimeFormatter.ofPattern("MM/dd/yyyy HH:mm:ss");
- private final static DateTimeFormatter formatter_dt19_eur = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss");
- private final static DateTimeFormatter formatter_dt19_de = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss");
- private final static DateTimeFormatter formatter_dt19_in = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss");
-
- private final static DateTimeFormatter formatter_d8 = DateTimeFormatter.ofPattern("yyyyMMdd");
- private final static DateTimeFormatter formatter_d10_tw = DateTimeFormatter.ofPattern("yyyy/MM/dd");
- private final static DateTimeFormatter formatter_d10_cn = DateTimeFormatter.ofPattern("yyyy年M月d日");
- private final static DateTimeFormatter formatter_d10_kr = DateTimeFormatter.ofPattern("yyyy년M월d일");
- private final static DateTimeFormatter formatter_d10_us = DateTimeFormatter.ofPattern("MM/dd/yyyy");
- private final static DateTimeFormatter formatter_d10_eur = DateTimeFormatter.ofPattern("dd/MM/yyyy");
- private final static DateTimeFormatter formatter_d10_de = DateTimeFormatter.ofPattern("dd.MM.yyyy");
- private final static DateTimeFormatter formatter_d10_in = DateTimeFormatter.ofPattern("dd-MM-yyyy");
+ public static final Jdk8DateCodec instance = new Jdk8DateCodec();
+
+ private final static String defaultPatttern = "yyyy-MM-dd HH:mm:ss";
+ private final static DateTimeFormatter defaultFormatter = DateTimeFormatter.ofPattern(defaultPatttern);
+ private final static DateTimeFormatter defaultFormatter_23 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
+ private final static DateTimeFormatter formatter_dt19_tw = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
+ private final static DateTimeFormatter formatter_dt19_cn = DateTimeFormatter.ofPattern("yyyy年M月d日 HH:mm:ss");
+ private final static DateTimeFormatter formatter_dt19_cn_1 = DateTimeFormatter.ofPattern("yyyy年M月d日 H时m分s秒");
+ private final static DateTimeFormatter formatter_dt19_kr = DateTimeFormatter.ofPattern("yyyy년M월d일 HH:mm:ss");
+ private final static DateTimeFormatter formatter_dt19_us = DateTimeFormatter.ofPattern("MM/dd/yyyy HH:mm:ss");
+ private final static DateTimeFormatter formatter_dt19_eur = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss");
+ private final static DateTimeFormatter formatter_dt19_de = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss");
+ private final static DateTimeFormatter formatter_dt19_in = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss");
+
+ private final static DateTimeFormatter formatter_d8 = DateTimeFormatter.ofPattern("yyyyMMdd");
+ private final static DateTimeFormatter formatter_d10_tw = DateTimeFormatter.ofPattern("yyyy/MM/dd");
+ private final static DateTimeFormatter formatter_d10_cn = DateTimeFormatter.ofPattern("yyyy年M月d日");
+ private final static DateTimeFormatter formatter_d10_kr = DateTimeFormatter.ofPattern("yyyy년M월d일");
+ private final static DateTimeFormatter formatter_d10_us = DateTimeFormatter.ofPattern("MM/dd/yyyy");
+ private final static DateTimeFormatter formatter_d10_eur = DateTimeFormatter.ofPattern("dd/MM/yyyy");
+ private final static DateTimeFormatter formatter_d10_de = DateTimeFormatter.ofPattern("dd.MM.yyyy");
+ private final static DateTimeFormatter formatter_d10_in = DateTimeFormatter.ofPattern("dd-MM-yyyy");
private final static DateTimeFormatter ISO_FIXED_FORMAT =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault());
private final static String formatter_iso8601_pattern = "yyyy-MM-dd'T'HH:mm:ss";
+ private final static String formatter_iso8601_pattern_23 = "yyyy-MM-dd'T'HH:mm:ss.SSS";
+ private final static String formatter_iso8601_pattern_29 = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSS";
private final static DateTimeFormatter formatter_iso8601 = DateTimeFormatter.ofPattern(formatter_iso8601_pattern);
@SuppressWarnings("unchecked")
@@ -98,20 +107,52 @@ public T deserialze(DefaultJSONParser parser, Type type, Object fieldName, S
return (T) localDate;
} else if (type == LocalTime.class) {
- LocalTime localDate;
+ LocalTime localTime;
if (text.length() == 23) {
LocalDateTime localDateTime = LocalDateTime.parse(text);
- localDate = LocalTime.of(localDateTime.getHour(), localDateTime.getMinute(),
+ localTime = LocalTime.of(localDateTime.getHour(), localDateTime.getMinute(),
localDateTime.getSecond(), localDateTime.getNano());
} else {
- localDate = LocalTime.parse(text);
+ boolean digit = true;
+ for (int i = 0; i < text.length(); ++i) {
+ char ch = text.charAt(i);
+ if (ch < '0' || ch > '9') {
+ digit = false;
+ break;
+ }
+ }
+
+ if (digit && text.length() > 8 && text.length() < 19) {
+ long epochMillis = Long.parseLong(text);
+ localTime = LocalDateTime
+ .ofInstant(
+ Instant.ofEpochMilli(epochMillis),
+ JSON.defaultTimeZone.toZoneId())
+ .toLocalTime();
+ } else {
+ localTime = LocalTime.parse(text);
+ }
}
- return (T) localDate;
+ return (T) localTime;
} else if (type == ZonedDateTime.class) {
if (formatter == defaultFormatter) {
formatter = ISO_FIXED_FORMAT;
}
+ if (formatter == null) {
+ if (text.length() <= 19) {
+ JSONScanner s = new JSONScanner(text);
+ TimeZone timeZone = parser.lexer.getTimeZone();
+ s.setTimeZone(timeZone);
+ boolean match = s.scanISO8601DateIfMatch(false);
+ if (match) {
+ Date date = s.getCalendar().getTime();
+ return (T) ZonedDateTime.ofInstant(date.toInstant(), timeZone.toZoneId());
+ }
+ }
+
+ }
+
ZonedDateTime zonedDateTime = parseZonedDateTime(text, formatter);
return (T) zonedDateTime;
@@ -133,9 +174,21 @@ public T deserialze(DefaultJSONParser parser, Type type, Object fieldName, S
return (T) period;
} else if (type == Duration.class) {
Duration duration = Duration.parse(text);
-
return (T) duration;
} else if (type == Instant.class) {
+ boolean digit = true;
+ for (int i = 0; i < text.length(); ++i) {
+ char ch = text.charAt(i);
+ if (ch < '0' || ch > '9') {
+ digit = false;
+ break;
+ }
+ }
+ if (digit && text.length() > 8 && text.length() < 19) {
+ long epochMillis = Long.parseLong(text);
+ return (T) Instant.ofEpochMilli(epochMillis);
+ }
+
Instant instant = Instant.parse(text);
return (T) instant;
@@ -144,11 +197,64 @@ public T deserialze(DefaultJSONParser parser, Type type, Object fieldName, S
long millis = lexer.longValue();
lexer.nextToken();
+ if ("unixtime".equals(format)) {
+ millis *= 1000;
+ } else if ("yyyyMMddHHmmss".equals(format)) {
+ int yyyy = (int) (millis / 10000000000L);
+ int MM = (int) ((millis / 100000000L) % 100);
+ int dd = (int) ((millis / 1000000L) % 100);
+ int HH = (int) ((millis / 10000L) % 100);
+ int mm = (int) ((millis / 100L) % 100);
+ int ss = (int) (millis % 100);
+
+ if (type == LocalDateTime.class) {
+ return (T) LocalDateTime.of(yyyy, MM, dd, HH, mm, ss);
+ }
+ }
+
if (type == LocalDateTime.class) {
- return (T) LocalDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneId.systemDefault());
+ return (T) LocalDateTime.ofInstant(Instant.ofEpochMilli(millis), JSON.defaultTimeZone.toZoneId());
+ }
+
+ if (type == LocalDate.class) {
+ return (T) LocalDateTime.ofInstant(Instant.ofEpochMilli(millis), JSON.defaultTimeZone.toZoneId()).toLocalDate();
+ }
+ if (type == LocalTime.class) {
+ return (T) LocalDateTime.ofInstant(Instant.ofEpochMilli(millis), JSON.defaultTimeZone.toZoneId()).toLocalTime();
+ }
+
+ if (type == ZonedDateTime.class) {
+ return (T) ZonedDateTime.ofInstant(Instant.ofEpochMilli(millis), JSON.defaultTimeZone.toZoneId());
+ }
+
+ if (type == Instant.class) {
+ return (T) Instant.ofEpochMilli(millis);
}
throw new UnsupportedOperationException();
+ } else if (lexer.token() == JSONToken.LBRACE) {
+ JSONObject object = parser.parseObject();
+
+ if (type == Instant.class) {
+ Object epochSecond = object.get("epochSecond");
+ Object nano = object.get("nano");
+ if (epochSecond instanceof Number && nano instanceof Number) {
+ return (T) Instant.ofEpochSecond(
+ TypeUtils.longExtractValue((Number) epochSecond)
+ , TypeUtils.longExtractValue((Number) nano));
+ }
+
+ if (epochSecond instanceof Number) {
+ return (T) Instant.ofEpochSecond(
+ TypeUtils.longExtractValue((Number) epochSecond));
+ }
+ } else if (type == Duration.class) {
+ Long seconds = object.getLong("seconds");
+ if (seconds != null) {
+ long nanos = object.getLongValue("nano");
+ return (T) Duration.ofSeconds(seconds, nanos);
+ }
+ }
} else {
throw new UnsupportedOperationException();
}
@@ -170,8 +276,6 @@ protected LocalDateTime parseDateTime(String text, DateTimeFormatter formatter)
} else if (c10 == ' ') {
formatter = defaultFormatter;
}
- } else if (c4 == '-' && c7 == '-') {
- formatter = defaultFormatter;
} else if (c4 == '/' && c7 == '/') { // tw yyyy/mm/dd
formatter = formatter_dt19_tw;
} else {
@@ -204,6 +308,23 @@ protected LocalDateTime parseDateTime(String text, DateTimeFormatter formatter)
}
}
}
+ } else if (text.length() == 23) {
+ char c4 = text.charAt(4);
+ char c7 = text.charAt(7);
+ char c10 = text.charAt(10);
+ char c13 = text.charAt(13);
+ char c16 = text.charAt(16);
+ char c19 = text.charAt(19);
+
+ if (c13 == ':'
+ && c16 == ':'
+ && c4 == '-'
+ && c7 == '-'
+ && c10 == ' '
+ && c19 == '.'
+ ) {
+ formatter = defaultFormatter_23;
+ }
}
if (text.length() >= 17) {
@@ -220,6 +341,27 @@ protected LocalDateTime parseDateTime(String text, DateTimeFormatter formatter)
}
}
+ if (formatter == null) {
+ JSONScanner dateScanner = new JSONScanner(text);
+ if (dateScanner.scanISO8601DateIfMatch(false)) {
+ Instant instant = dateScanner.getCalendar().toInstant();
+ return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
+ }
+
+ boolean digit = true;
+ for (int i = 0; i < text.length(); ++i) {
+ char ch = text.charAt(i);
+ if (ch < '0' || ch > '9') {
+ digit = false;
+ break;
+ }
+ }
+ if (digit && text.length() > 8 && text.length() < 19) {
+ long epochMillis = Long.parseLong(text);
+ return LocalDateTime.ofInstant(Instant.ofEpochMilli(epochMillis), JSON.defaultTimeZone.toZoneId());
+ }
+ }
+
return formatter == null ? //
LocalDateTime.parse(text) //
: LocalDateTime.parse(text, formatter);
@@ -275,6 +417,23 @@ protected LocalDate parseLocalDate(String text, String format, DateTimeFormatter
formatter = formatter_d10_kr;
}
}
+
+ boolean digit = true;
+ for (int i = 0; i < text.length(); ++i) {
+ char ch = text.charAt(i);
+ if (ch < '0' || ch > '9') {
+ digit = false;
+ break;
+ }
+ }
+ if (digit && text.length() > 8 && text.length() < 19) {
+ long epochMillis = Long.parseLong(text);
+ return LocalDateTime
+ .ofInstant(
+ Instant.ofEpochMilli(epochMillis),
+ JSON.defaultTimeZone.toZoneId())
+ .toLocalDate();
+ }
}
return formatter == null ? //
@@ -297,8 +456,6 @@ protected ZonedDateTime parseZonedDateTime(String text, DateTimeFormatter format
} else if (c10 == ' ') {
formatter = defaultFormatter;
}
- } else if (c4 == '-' && c7 == '-') {
- formatter = defaultFormatter;
} else if (c4 == '/' && c7 == '/') { // tw yyyy/mm/dd
formatter = formatter_dt19_tw;
} else {
@@ -345,6 +502,19 @@ protected ZonedDateTime parseZonedDateTime(String text, DateTimeFormatter format
formatter = formatter_dt19_kr;
}
}
+
+ boolean digit = true;
+ for (int i = 0; i < text.length(); ++i) {
+ char ch = text.charAt(i);
+ if (ch < '0' || ch > '9') {
+ digit = false;
+ break;
+ }
+ }
+ if (digit && text.length() > 8 && text.length() < 19) {
+ long epochMillis = Long.parseLong(text);
+ return ZonedDateTime.ofInstant(Instant.ofEpochMilli(epochMillis), JSON.defaultTimeZone.toZoneId());
+ }
}
return formatter == null ? //
@@ -371,20 +541,32 @@ public void write(JSONSerializer serializer, Object object, Object fieldName, Ty
LocalDateTime dateTime = (LocalDateTime) object;
String format = serializer.getDateFormatPattern();
- if (format == null && (features & mask) != 0 || serializer.isEnabled(SerializerFeature.UseISO8601DateFormat)) {
- format = formatter_iso8601_pattern;
+ if (format == null) {
+ if ((features & mask) != 0 || serializer.isEnabled(SerializerFeature.UseISO8601DateFormat)) {
+ format = formatter_iso8601_pattern;
+ } else if (serializer.isEnabled(SerializerFeature.WriteDateUseDateFormat)) {
+ if (serializer.getFastJsonConfigDateFormatPattern() != null &&
+ serializer.getFastJsonConfigDateFormatPattern().length() > 0){
+ format = serializer.getFastJsonConfigDateFormatPattern();
+ }else{
+ format = JSON.DEFFAULT_DATE_FORMAT;
+ }
+ } else {
+ int nano = dateTime.getNano();
+ if (nano == 0) {
+ format = formatter_iso8601_pattern;
+ } else if (nano % 1000000 == 0) {
+ format = formatter_iso8601_pattern_23;
+ } else {
+ format = formatter_iso8601_pattern_29;
+ }
+ }
}
- if (dateTime.getNano() == 0 || format != null) {
-
- if (format == null) {
- format = JSON.DEFFAULT_DATE_FORMAT;
- }
+ if (format != null) {
write(out, dateTime, format);
} else {
- out.writeString(object.toString());
-// format = JSON.DEFFAULT_LOCAL_DATE_TIME_FORMAT;
-// write(out, dateTime, format);
+ out.writeLong(dateTime.atZone(JSON.defaultTimeZone.toZoneId()).toInstant().toEpochMilli());
}
} else {
out.writeString(object.toString());
@@ -400,6 +582,35 @@ public void write(JSONSerializer serializer, Object object, BeanContext context)
private void write(SerializeWriter out, TemporalAccessor object, String format) {
DateTimeFormatter formatter;
+ if ("unixtime".equals(format)) {
+ Instant instant = null;
+ if (object instanceof ChronoZonedDateTime) {
+ long seconds = ((ChronoZonedDateTime) object).toEpochSecond();
+ out.writeInt((int) seconds);
+ return;
+ }
+
+ if (object instanceof LocalDateTime) {
+ long seconds = ((LocalDateTime) object).atZone(JSON.defaultTimeZone.toZoneId()).toEpochSecond();
+ out.writeInt((int) seconds);
+ return;
+ }
+ }
+
+ if ("millis".equals(format)) {
+ Instant instant = null;
+ if (object instanceof ChronoZonedDateTime) {
+ instant = ((ChronoZonedDateTime) object).toInstant();
+ } else if (object instanceof LocalDateTime) {
+ instant = ((LocalDateTime) object).atZone(JSON.defaultTimeZone.toZoneId()).toInstant();
+ }
+ if (instant != null) {
+ long millis = instant.toEpochMilli();
+ out.writeLong(millis);
+ return;
+ }
+ }
+
if (format == formatter_iso8601_pattern) {
formatter = formatter_iso8601;
} else {
@@ -409,4 +620,17 @@ private void write(SerializeWriter out, TemporalAccessor object, String format)
String text = formatter.format((TemporalAccessor) object);
out.writeString(text);
}
+
+ public static Object castToLocalDateTime(Object value, String format) {
+ if (value == null) {
+ return null;
+ }
+
+ if (format == null) {
+ format = "yyyy-MM-dd HH:mm:ss";
+ }
+
+ DateTimeFormatter df = DateTimeFormatter.ofPattern(format);
+ return LocalDateTime.parse(value.toString(), df);
+ }
}
diff --git a/src/main/java/com/alibaba/fastjson/parser/deserializer/MapDeserializer.java b/src/main/java/com/alibaba/fastjson/parser/deserializer/MapDeserializer.java
index 7d61405bee..e7a55a505e 100644
--- a/src/main/java/com/alibaba/fastjson/parser/deserializer/MapDeserializer.java
+++ b/src/main/java/com/alibaba/fastjson/parser/deserializer/MapDeserializer.java
@@ -1,6 +1,5 @@
package com.alibaba.fastjson.parser.deserializer;
-import java.lang.reflect.Constructor;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
@@ -13,15 +12,13 @@
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.*;
import com.alibaba.fastjson.parser.DefaultJSONParser.ResolveTask;
-import com.alibaba.fastjson.serializer.CollectionCodec;
-import com.alibaba.fastjson.util.TypeUtils;
-public class MapDeserializer implements ObjectDeserializer {
+public class MapDeserializer extends ContextObjectDeserializer implements ObjectDeserializer {
public static MapDeserializer instance = new MapDeserializer();
-
-
+
@SuppressWarnings("unchecked")
- public T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
+ public T deserialze(DefaultJSONParser parser, Type type, Object fieldName, String format, int features)
+ {
if (type == JSONObject.class && parser.getFieldTypeResolver() == null) {
return (T) parser.parseObject();
}
@@ -32,20 +29,33 @@ public T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
return null;
}
- Map map = createMap(type);
+ boolean unmodifiableMap = type instanceof Class
+ && "java.util.Collections$UnmodifiableMap".equals(((Class) type).getName());
+
+ Map map = (lexer.getFeatures() & Feature.OrderedField.mask) != 0
+ ? createMap(type, lexer.getFeatures())
+ : createMap(type);
ParseContext context = parser.getContext();
try {
parser.setContext(context, map, fieldName);
- return (T) deserialze(parser, type, fieldName, map);
+ T t = (T) deserialze(parser, type, fieldName, map, features);
+ if (unmodifiableMap) {
+ t = (T) Collections.unmodifiableMap((Map) t);
+ }
+ return t;
} finally {
parser.setContext(context);
}
}
- @SuppressWarnings({ "rawtypes", "unchecked" })
protected Object deserialze(DefaultJSONParser parser, Type type, Object fieldName, Map map) {
+ return deserialze(parser, type, fieldName, map, 0);
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ protected Object deserialze(DefaultJSONParser parser, Type type, Object fieldName, Map map, int features) {
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
Type keyType = parameterizedType.getActualTypeArguments()[0];
@@ -56,7 +66,7 @@ protected Object deserialze(DefaultJSONParser parser, Type type, Object fieldNam
valueType = parameterizedType.getActualTypeArguments()[1];
}
if (String.class == keyType) {
- return parseMap(parser, (Map) map, valueType, fieldName);
+ return parseMap(parser, (Map) map, valueType, fieldName, features);
} else {
return parseMap(parser, map, keyType, valueType, fieldName);
}
@@ -64,13 +74,24 @@ protected Object deserialze(DefaultJSONParser parser, Type type, Object fieldNam
return parser.parseObject(map, fieldName);
}
}
+
+ public static Map parseMap(DefaultJSONParser parser, Map map, Type valueType, Object fieldName) {
+ return parseMap(parser, map, valueType, fieldName, 0);
+ }
@SuppressWarnings("rawtypes")
- public static Map parseMap(DefaultJSONParser parser, Map map, Type valueType, Object fieldName) {
+ public static Map parseMap(DefaultJSONParser parser, Map map, Type valueType, Object fieldName, int features) {
JSONLexer lexer = parser.lexer;
int token = lexer.token();
if (token != JSONToken.LBRACE) {
+ if (token == JSONToken.LITERAL_STRING) {
+ String stringVal = lexer.stringVal();
+ if (stringVal.length() == 0 || stringVal.equals("null")) {
+ return null;
+ }
+ }
+
String msg = "syntax error, expect {, actual " + lexer.tokenName();
if (fieldName instanceof String) {
msg += ", fieldName ";
@@ -150,11 +171,29 @@ public static Map parseMap(DefaultJSONParser parser, Map map, Ty
lexer.resetStringPosition();
- if (key == JSON.DEFAULT_TYPE_KEY && !lexer.isEnabled(Feature.DisableSpecialKeyDetect)) {
+ if (key == JSON.DEFAULT_TYPE_KEY
+ && !lexer.isEnabled(Feature.DisableSpecialKeyDetect)
+ && !Feature.isEnabled(features, Feature.DisableSpecialKeyDetect)
+ ) {
String typeName = lexer.scanSymbol(parser.getSymbolTable(), '"');
final ParserConfig config = parser.getConfig();
- Class> clazz = config.checkAutoType(typeName, null, lexer.getFeatures());
+ Class> clazz;
+
+ if (typeName.equals("java.util.HashMap")) {
+ clazz = java.util.HashMap.class;
+ } else if (typeName.equals("java.util.LinkedHashMap")) {
+ clazz = java.util.LinkedHashMap.class;
+ } else if (config.isSafeMode()) {
+ clazz = java.util.HashMap.class;
+ } else {
+ try {
+ clazz = config.checkAutoType(typeName, null, lexer.getFeatures());
+ } catch (JSONException ex) {
+ // skip
+ clazz = java.util.HashMap.class;
+ }
+ }
if (Map.class.isAssignableFrom(clazz) ) {
lexer.nextToken(JSONToken.COMMA);
@@ -286,7 +325,18 @@ public static Object parseMap(DefaultJSONParser parser, Map map,
lexer.nextToken(keyDeserializer.getFastMatchToken());
}
- Object key = keyDeserializer.deserialze(parser, keyType, null);
+ Object key;
+ if (lexer.token() == JSONToken.LITERAL_STRING
+ && keyDeserializer instanceof JavaBeanDeserializer
+ ) {
+ String keyStrValue = lexer.stringVal();
+ lexer.nextToken();
+ DefaultJSONParser keyParser = new DefaultJSONParser(keyStrValue, parser.getConfig(), parser.getLexer().getFeatures());
+ keyParser.setDateFormat(parser.getDateFomartPattern());
+ key = keyDeserializer.deserialze(keyParser, keyType, null);
+ } else {
+ key = keyDeserializer.deserialze(parser, keyType, null);
+ }
if (lexer.token() != JSONToken.COLON) {
throw new JSONException("syntax error, expect :, actual " + lexer.token());
@@ -310,8 +360,12 @@ public static Object parseMap(DefaultJSONParser parser, Map map,
return map;
}
+ public Map createMap(Type type) {
+ return createMap(type, JSON.DEFAULT_GENERATE_FEATURE);
+ }
+
@SuppressWarnings({ "unchecked", "rawtypes" })
- protected Map createMap(Type type) {
+ public Map createMap(Type type, int featrues) {
if (type == Properties.class) {
return new Properties();
}
@@ -332,10 +386,16 @@ protected Map createMap(Type type) {
return new ConcurrentHashMap();
}
- if (type == Map.class || type == HashMap.class) {
+ if (type == Map.class) {
+ return (featrues & Feature.OrderedField.mask) != 0
+ ? new LinkedHashMap()
+ : new HashMap();
+ }
+
+ if (type == HashMap.class) {
return new HashMap();
}
-
+
if (type == LinkedHashMap.class) {
return new LinkedHashMap();
}
@@ -349,13 +409,17 @@ protected Map createMap(Type type) {
return new EnumMap((Class) actualArgs[0]);
}
- return createMap(rawType);
+ return createMap(rawType, featrues);
}
Class> clazz = (Class>) type;
if (clazz.isInterface()) {
throw new JSONException("unsupport type " + type);
}
+
+ if ("java.util.Collections$UnmodifiableMap".equals(clazz.getName())) {
+ return new HashMap();
+ }
try {
return (Map) clazz.newInstance();
diff --git a/src/main/java/com/alibaba/fastjson/parser/deserializer/NumberDeserializer.java b/src/main/java/com/alibaba/fastjson/parser/deserializer/NumberDeserializer.java
old mode 100755
new mode 100644
index d23c063374..ac516cf951
--- a/src/main/java/com/alibaba/fastjson/parser/deserializer/NumberDeserializer.java
+++ b/src/main/java/com/alibaba/fastjson/parser/deserializer/NumberDeserializer.java
@@ -4,8 +4,8 @@
import java.math.BigDecimal;
import com.alibaba.fastjson.JSONException;
-import com.alibaba.fastjson.annotation.JSONType;
import com.alibaba.fastjson.parser.DefaultJSONParser;
+import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.JSONLexer;
import com.alibaba.fastjson.parser.JSONToken;
import com.alibaba.fastjson.util.TypeUtils;
@@ -55,21 +55,28 @@ public T deserialze(DefaultJSONParser parser, Type clazz, Object fieldName)
return (T) Double.valueOf(Double.parseDouble(val));
}
- BigDecimal val = lexer.decimalValue();
- lexer.nextToken(JSONToken.COMMA);
-
if (clazz == short.class || clazz == Short.class) {
- if (val.compareTo(BigDecimal.valueOf(Short.MAX_VALUE)) > 0 || val.compareTo(BigDecimal.valueOf(Short.MIN_VALUE)) < 0) {
- throw new JSONException("short overflow : " + val);
- }
- return (T) Short.valueOf(val.shortValue());
+ BigDecimal val = lexer.decimalValue();
+ lexer.nextToken(JSONToken.COMMA);
+ short shortValue = TypeUtils.shortValue(val);
+ return (T) Short.valueOf(shortValue);
}
if (clazz == byte.class || clazz == Byte.class) {
- return (T) Byte.valueOf(val.byteValue());
+ BigDecimal val = lexer.decimalValue();
+ lexer.nextToken(JSONToken.COMMA);
+ byte byteValue = TypeUtils.byteValue(val);
+ return (T) Byte.valueOf(byteValue);
}
- return (T) val;
+ BigDecimal val = lexer.decimalValue();
+ lexer.nextToken(JSONToken.COMMA);
+
+ if (lexer.isEnabled(Feature.UseBigDecimal)) {
+ return (T) val;
+ } else {
+ return (T) Double.valueOf(val.doubleValue());
+ }
}
if (lexer.token() == JSONToken.IDENTIFIER && "NaN".equals(lexer.stringVal())) {
diff --git a/src/main/java/com/alibaba/fastjson/parser/deserializer/ObjectDeserializer.java b/src/main/java/com/alibaba/fastjson/parser/deserializer/ObjectDeserializer.java
old mode 100755
new mode 100644
diff --git a/src/main/java/com/alibaba/fastjson/parser/deserializer/SqlDateDeserializer.java b/src/main/java/com/alibaba/fastjson/parser/deserializer/SqlDateDeserializer.java
old mode 100755
new mode 100644
index 254a36858e..be01a29548
--- a/src/main/java/com/alibaba/fastjson/parser/deserializer/SqlDateDeserializer.java
+++ b/src/main/java/com/alibaba/fastjson/parser/deserializer/SqlDateDeserializer.java
@@ -1,14 +1,17 @@
package com.alibaba.fastjson.parser.deserializer;
import java.lang.reflect.Type;
+import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.Date;
+import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.parser.DefaultJSONParser;
import com.alibaba.fastjson.parser.JSONScanner;
import com.alibaba.fastjson.parser.JSONToken;
+import com.alibaba.fastjson.util.TypeUtils;
public class SqlDateDeserializer extends AbstractDateDeserializer implements ObjectDeserializer {
@@ -37,6 +40,8 @@ protected T cast(DefaultJSONParser parser, Type clazz, Object fieldName, Obj
if (val instanceof java.util.Date) {
val = new java.sql.Date(((Date) val).getTime());
+ } else if (val instanceof BigDecimal) {
+ val = (T) new java.sql.Date(TypeUtils.longValue((BigDecimal) val));
} else if (val instanceof Number) {
val = (T) new java.sql.Date(((Number) val).longValue());
} else if (val instanceof String) {
@@ -86,6 +91,10 @@ protected T castTimestamp(DefaultJSONParser parser, Type clazz, Object field
return (T) new java.sql.Timestamp(((Date) val).getTime());
}
+ if (val instanceof BigDecimal) {
+ return (T) new java.sql.Timestamp(TypeUtils.longValue((BigDecimal) val));
+ }
+
if (val instanceof Number) {
return (T) new java.sql.Timestamp(((Number) val).longValue());
}
@@ -99,10 +108,22 @@ protected T castTimestamp(DefaultJSONParser parser, Type clazz, Object field
long longVal;
JSONScanner dateLexer = new JSONScanner(strVal);
try {
- if (dateLexer.scanISO8601DateIfMatch()) {
+ if (strVal.length() > 19
+ && strVal.charAt(4) == '-'
+ && strVal.charAt(7) == '-'
+ && strVal.charAt(10) == ' '
+ && strVal.charAt(13) == ':'
+ && strVal.charAt(16) == ':'
+ && strVal.charAt(19) == '.') {
+ String dateFomartPattern = parser.getDateFomartPattern();
+ if (dateFomartPattern.length() != strVal.length() && dateFomartPattern == JSON.DEFFAULT_DATE_FORMAT) {
+ return (T) java.sql.Timestamp.valueOf(strVal);
+ }
+ }
+
+ if (dateLexer.scanISO8601DateIfMatch(false)) {
longVal = dateLexer.getCalendar().getTimeInMillis();
} else {
-
DateFormat dateFormat = parser.getDateFormat();
try {
java.util.Date date = (java.util.Date) dateFormat.parse(strVal);
diff --git a/src/main/java/com/alibaba/fastjson/parser/deserializer/StackTraceElementDeserializer.java b/src/main/java/com/alibaba/fastjson/parser/deserializer/StackTraceElementDeserializer.java
old mode 100755
new mode 100644
diff --git a/src/main/java/com/alibaba/fastjson/parser/deserializer/ThrowableDeserializer.java b/src/main/java/com/alibaba/fastjson/parser/deserializer/ThrowableDeserializer.java
old mode 100755
new mode 100644
index 6b07590929..301ab86b79
--- a/src/main/java/com/alibaba/fastjson/parser/deserializer/ThrowableDeserializer.java
+++ b/src/main/java/com/alibaba/fastjson/parser/deserializer/ThrowableDeserializer.java
@@ -1,7 +1,6 @@
package com.alibaba.fastjson.parser.deserializer;
import java.lang.reflect.Constructor;
-import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
@@ -13,7 +12,7 @@
import com.alibaba.fastjson.parser.JSONLexer;
import com.alibaba.fastjson.parser.JSONToken;
import com.alibaba.fastjson.parser.ParserConfig;
-import com.alibaba.fastjson.serializer.JavaBeanSerializer;
+import com.alibaba.fastjson.util.FieldInfo;
import com.alibaba.fastjson.util.TypeUtils;
public class ThrowableDeserializer extends JavaBeanDeserializer {
@@ -142,13 +141,19 @@ public T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
}
}
- for (Map.Entry entry : otherValues.entrySet()) {
- String key = entry.getKey();
- Object value = entry.getValue();
-
- FieldDeserializer fieldDeserializer = exBeanDeser.getFieldDeserializer(key);
- if (fieldDeserializer != null) {
- fieldDeserializer.setValue(ex, value);
+ if (exBeanDeser != null) {
+ for (Map.Entry entry : otherValues.entrySet()) {
+ String key = entry.getKey();
+ Object value = entry.getValue();
+
+ FieldDeserializer fieldDeserializer = exBeanDeser.getFieldDeserializer(key);
+ if (fieldDeserializer != null) {
+ FieldInfo fieldInfo = fieldDeserializer.fieldInfo;
+ if (!fieldInfo.fieldClass.isInstance(value)) {
+ value = TypeUtils.cast(value, fieldInfo.fieldType, parser.getConfig());
+ }
+ fieldDeserializer.setValue(ex, value);
+ }
}
}
}
diff --git a/src/main/java/com/alibaba/fastjson/parser/deserializer/TimeDeserializer.java b/src/main/java/com/alibaba/fastjson/parser/deserializer/TimeDeserializer.java
old mode 100755
new mode 100644
index e938969ee6..b8030a48be
--- a/src/main/java/com/alibaba/fastjson/parser/deserializer/TimeDeserializer.java
+++ b/src/main/java/com/alibaba/fastjson/parser/deserializer/TimeDeserializer.java
@@ -1,12 +1,14 @@
package com.alibaba.fastjson.parser.deserializer;
import java.lang.reflect.Type;
+import java.math.BigDecimal;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.parser.DefaultJSONParser;
import com.alibaba.fastjson.parser.JSONLexer;
import com.alibaba.fastjson.parser.JSONScanner;
import com.alibaba.fastjson.parser.JSONToken;
+import com.alibaba.fastjson.util.TypeUtils;
public class TimeDeserializer implements ObjectDeserializer {
@@ -47,6 +49,8 @@ public T deserialze(DefaultJSONParser parser, Type clazz, Object fieldName)
if (val instanceof java.sql.Time) {
return (T) val;
+ } else if (val instanceof BigDecimal) {
+ return (T) new java.sql.Time(TypeUtils.longValue((BigDecimal) val));
} else if (val instanceof Number) {
return (T) new java.sql.Time(((Number) val).longValue());
} else if (val instanceof String) {
diff --git a/src/main/java/com/alibaba/fastjson/serializer/ASMSerializerFactory.java b/src/main/java/com/alibaba/fastjson/serializer/ASMSerializerFactory.java
old mode 100755
new mode 100644
index a9f7967ac8..e65bbece7d
--- a/src/main/java/com/alibaba/fastjson/serializer/ASMSerializerFactory.java
+++ b/src/main/java/com/alibaba/fastjson/serializer/ASMSerializerFactory.java
@@ -109,7 +109,7 @@ public JavaBeanSerializer createJavaBeanSerializer(SerializeBeanInfo beanInfo) t
throw new JSONException("unsupportd class " + clazz.getName());
}
- JSONType jsonType = TypeUtils.getAnnotation(clazz,JSONType.class);
+ JSONType jsonType = TypeUtils.getAnnotation(clazz, JSONType.class);
FieldInfo[] unsortedGetters = beanInfo.fields;
@@ -136,9 +136,17 @@ public JavaBeanSerializer createJavaBeanSerializer(SerializeBeanInfo beanInfo) t
}
String className = "ASMSerializer_" + seed.incrementAndGet() + "_" + clazz.getSimpleName();
- String packageName = ASMSerializerFactory.class.getPackage().getName();
- String classNameType = packageName.replace('.', '/') + "/" + className;
- String classNameFull = packageName + "." + className;
+ String classNameType;
+ String classNameFull;
+ Package pkg = ASMSerializerFactory.class.getPackage();
+ if (pkg != null) {
+ String packageName = pkg.getName();
+ classNameType = packageName.replace('.', '/') + "/" + className;
+ classNameFull = packageName + "." + className;
+ } else {
+ classNameType = className;
+ classNameFull = className;
+ }
ClassWriter cw = new ClassWriter();
cw.visit(V1_5 //
@@ -408,6 +416,23 @@ public JavaBeanSerializer createJavaBeanSerializer(SerializeBeanInfo beanInfo) t
private void generateWriteAsArray(Class> clazz, MethodVisitor mw, FieldInfo[] getters,
Context context) throws Exception {
+ Label nonPropertyFilters_ = new Label();
+ mw.visitVarInsn(ALOAD, Context.serializer);
+ mw.visitVarInsn(ALOAD, 0);
+ mw.visitMethodInsn(INVOKEVIRTUAL, JSONSerializer, "hasPropertyFilters", "(" + SerializeFilterable_desc + ")Z");
+ mw.visitJumpInsn(IFNE, nonPropertyFilters_);
+ mw.visitVarInsn(ALOAD, 0);
+ mw.visitVarInsn(ALOAD, 1);
+ mw.visitVarInsn(ALOAD, 2);
+ mw.visitVarInsn(ALOAD, 3);
+ mw.visitVarInsn(ALOAD, 4);
+ mw.visitVarInsn(ILOAD, 5);
+ mw.visitMethodInsn(INVOKESPECIAL, JavaBeanSerializer,
+ "writeNoneASM", "(L" + JSONSerializer
+ + ";Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/reflect/Type;I)V");
+ mw.visitInsn(RETURN);
+
+ mw.visitLabel(nonPropertyFilters_);
mw.visitVarInsn(ALOAD, context.var("out"));
mw.visitVarInsn(BIPUSH, '[');
mw.visitMethodInsn(INVOKEVIRTUAL, SerializeWriter, "write", "(I)V");
@@ -773,6 +798,7 @@ private void generateWriteMethod(Class> clazz, MethodVisitor mw, FieldInfo[] g
for (FieldInfo getter : getters) {
if (getter.method != null) {
hasMethod = true;
+ break;
}
}
@@ -1961,7 +1987,10 @@ private void _if_write_null(MethodVisitor mw, FieldInfo fieldInfo, Context conte
int features = 0;
if (annotation != null) {
features = SerializerFeature.of(annotation.serialzeFeatures());
- ;
+ }
+ JSONType jsonType = context.beanInfo.jsonType;
+ if (jsonType != null) {
+ features |= SerializerFeature.of(jsonType.serialzeFeatures());
}
int writeNullFeatures;
diff --git a/src/main/java/com/alibaba/fastjson/serializer/AfterFilter.java b/src/main/java/com/alibaba/fastjson/serializer/AfterFilter.java
index 0c032fcf6d..7e61e23b4b 100644
--- a/src/main/java/com/alibaba/fastjson/serializer/AfterFilter.java
+++ b/src/main/java/com/alibaba/fastjson/serializer/AfterFilter.java
@@ -11,17 +11,23 @@ public abstract class AfterFilter implements SerializeFilter {
private final static Character COMMA = Character.valueOf(',');
final char writeAfter(JSONSerializer serializer, Object object, char seperator) {
+ JSONSerializer last = serializerLocal.get();
serializerLocal.set(serializer);
seperatorLocal.set(seperator);
writeAfter(object);
- serializerLocal.set(null);
+ serializerLocal.set(last);
return seperatorLocal.get();
}
protected final void writeKeyValue(String key, Object value) {
JSONSerializer serializer = serializerLocal.get();
char seperator = seperatorLocal.get();
+
+ boolean ref = serializer.containsReference(value);
serializer.writeKeyValue(seperator, key, value);
+ if (!ref && serializer.references != null) {
+ serializer.references.remove(value);
+ }
if (seperator != ',') {
seperatorLocal.set(COMMA);
}
diff --git a/src/main/java/com/alibaba/fastjson/serializer/AnnotationSerializer.java b/src/main/java/com/alibaba/fastjson/serializer/AnnotationSerializer.java
index 66689cdc9e..b2e08d992e 100644
--- a/src/main/java/com/alibaba/fastjson/serializer/AnnotationSerializer.java
+++ b/src/main/java/com/alibaba/fastjson/serializer/AnnotationSerializer.java
@@ -1,8 +1,8 @@
package com.alibaba.fastjson.serializer;
import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
-import sun.reflect.annotation.AnnotationType;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
@@ -15,6 +15,11 @@
* Created by wenshao on 10/05/2017.
*/
public class AnnotationSerializer implements ObjectSerializer {
+ private static volatile Class sun_AnnotationType = null;
+ private static volatile boolean sun_AnnotationType_error = false;
+ private static volatile Method sun_AnnotationType_getInstance = null;
+ private static volatile Method sun_AnnotationType_members = null;
+
public static AnnotationSerializer instance = new AnnotationSerializer();
public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
@@ -22,8 +27,58 @@ public void write(JSONSerializer serializer, Object object, Object fieldName, Ty
Class[] interfaces = objClass.getInterfaces();
if (interfaces.length == 1 && interfaces[0].isAnnotation()) {
Class annotationClass = interfaces[0];
- AnnotationType type = AnnotationType.getInstance(annotationClass);
- Map members = type.members();
+
+ if (sun_AnnotationType == null && !sun_AnnotationType_error) {
+ try {
+ sun_AnnotationType = Class.forName("sun.reflect.annotation.AnnotationType");
+ } catch (Throwable ex) {
+ sun_AnnotationType_error = true;
+ throw new JSONException("not support Type Annotation.", ex);
+ }
+ }
+
+ if (sun_AnnotationType == null) {
+ throw new JSONException("not support Type Annotation.");
+ }
+
+ if (sun_AnnotationType_getInstance == null && !sun_AnnotationType_error) {
+ try {
+ sun_AnnotationType_getInstance = sun_AnnotationType.getMethod("getInstance", Class.class);
+ } catch (Throwable ex) {
+ sun_AnnotationType_error = true;
+ throw new JSONException("not support Type Annotation.", ex);
+ }
+ }
+
+ if (sun_AnnotationType_members == null && !sun_AnnotationType_error) {
+ try {
+ sun_AnnotationType_members = sun_AnnotationType.getMethod("members");
+ } catch (Throwable ex) {
+ sun_AnnotationType_error = true;
+ throw new JSONException("not support Type Annotation.", ex);
+ }
+ }
+
+ if (sun_AnnotationType_getInstance == null || sun_AnnotationType_error) {
+ throw new JSONException("not support Type Annotation.");
+ }
+
+ Object type;
+ try {
+ type = sun_AnnotationType_getInstance.invoke(null, annotationClass);
+ } catch (Throwable ex) {
+ sun_AnnotationType_error = true;
+ throw new JSONException("not support Type Annotation.", ex);
+ }
+
+ Map members;
+ try {
+ members = (Map) sun_AnnotationType_members.invoke(type);
+ } catch (Throwable ex) {
+ sun_AnnotationType_error = true;
+ throw new JSONException("not support Type Annotation.", ex);
+ }
+
JSONObject json = new JSONObject(members.size());
Iterator> iterator = members.entrySet().iterator();
Map.Entry entry;
diff --git a/src/main/java/com/alibaba/fastjson/serializer/AppendableSerializer.java b/src/main/java/com/alibaba/fastjson/serializer/AppendableSerializer.java
old mode 100755
new mode 100644
diff --git a/src/main/java/com/alibaba/fastjson/serializer/ArraySerializer.java b/src/main/java/com/alibaba/fastjson/serializer/ArraySerializer.java
old mode 100755
new mode 100644
index 43ec5df57f..dedad4d4d1
--- a/src/main/java/com/alibaba/fastjson/serializer/ArraySerializer.java
+++ b/src/main/java/com/alibaba/fastjson/serializer/ArraySerializer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2017 Alibaba Group.
+ * Copyright 1999-2018 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/src/main/java/com/alibaba/fastjson/serializer/AtomicCodec.java b/src/main/java/com/alibaba/fastjson/serializer/AtomicCodec.java
old mode 100755
new mode 100644
index eae108c76f..1b309cfafc
--- a/src/main/java/com/alibaba/fastjson/serializer/AtomicCodec.java
+++ b/src/main/java/com/alibaba/fastjson/serializer/AtomicCodec.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2017 Alibaba Group.
+ * Copyright 1999-2018 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/src/main/java/com/alibaba/fastjson/serializer/AutowiredObjectSerializer.java b/src/main/java/com/alibaba/fastjson/serializer/AutowiredObjectSerializer.java
old mode 100755
new mode 100644
index 00d97848e7..7bc3e1db7f
--- a/src/main/java/com/alibaba/fastjson/serializer/AutowiredObjectSerializer.java
+++ b/src/main/java/com/alibaba/fastjson/serializer/AutowiredObjectSerializer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2017 Alibaba Group.
+ * Copyright 1999-2018 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/src/main/java/com/alibaba/fastjson/serializer/BeforeFilter.java b/src/main/java/com/alibaba/fastjson/serializer/BeforeFilter.java
index 34ed06041d..7b4de56c5d 100644
--- a/src/main/java/com/alibaba/fastjson/serializer/BeforeFilter.java
+++ b/src/main/java/com/alibaba/fastjson/serializer/BeforeFilter.java
@@ -8,17 +8,24 @@ public abstract class BeforeFilter implements SerializeFilter {
private final static Character COMMA = Character.valueOf(',');
final char writeBefore(JSONSerializer serializer, Object object, char seperator) {
+ JSONSerializer last = serializerLocal.get();
serializerLocal.set(serializer);
seperatorLocal.set(seperator);
writeBefore(object);
- serializerLocal.set(null);
+ serializerLocal.set(last);
return seperatorLocal.get();
}
protected final void writeKeyValue(String key, Object value) {
JSONSerializer serializer = serializerLocal.get();
char seperator = seperatorLocal.get();
+
+ boolean ref = serializer.references.containsKey(value);
serializer.writeKeyValue(seperator, key, value);
+ if (!ref) {
+ serializer.references.remove(value);
+ }
+
if (seperator != ',') {
seperatorLocal.set(COMMA);
}
diff --git a/src/main/java/com/alibaba/fastjson/serializer/BigDecimalCodec.java b/src/main/java/com/alibaba/fastjson/serializer/BigDecimalCodec.java
old mode 100755
new mode 100644
index 7431a4066f..92defb01d5
--- a/src/main/java/com/alibaba/fastjson/serializer/BigDecimalCodec.java
+++ b/src/main/java/com/alibaba/fastjson/serializer/BigDecimalCodec.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2017 Alibaba Group.
+ * Copyright 1999-2018 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,6 +30,8 @@
* @author wenshao[szujobs@hotmail.com]
*/
public class BigDecimalCodec implements ObjectSerializer, ObjectDeserializer {
+ final static BigDecimal LOW = BigDecimal.valueOf(-9007199254740991L);
+ final static BigDecimal HIGH = BigDecimal.valueOf(9007199254740991L);
public final static BigDecimalCodec instance = new BigDecimalCodec();
@@ -40,13 +42,27 @@ public void write(JSONSerializer serializer, Object object, Object fieldName, Ty
out.writeNull(SerializerFeature.WriteNullNumberAsZero);
} else {
BigDecimal val = (BigDecimal) object;
+ int scale = val.scale();
String outText;
- if (out.isEnabled(SerializerFeature.WriteBigDecimalAsPlain)) {
+ if (SerializerFeature.isEnabled(features, out.features, SerializerFeature.WriteBigDecimalAsPlain)
+ && scale >= -100 && scale < 100) {
outText = val.toPlainString();
} else {
outText = val.toString();
}
+
+ if (scale == 0) {
+ if (outText.length() >= 16
+ && SerializerFeature.isEnabled(features, out.features, SerializerFeature.BrowserCompatible)
+ && (val.compareTo(LOW) < 0
+ || val.compareTo(HIGH) > 0))
+ {
+ out.writeString(outText);
+ return;
+ }
+ }
+
out.write(outText);
if (out.isEnabled(SerializerFeature.WriteClassName) && fieldType != BigDecimal.class && val.scale() == 0) {
diff --git a/src/main/java/com/alibaba/fastjson/serializer/BigIntegerCodec.java b/src/main/java/com/alibaba/fastjson/serializer/BigIntegerCodec.java
old mode 100755
new mode 100644
index 55e3cbf891..45b30a1b8b
--- a/src/main/java/com/alibaba/fastjson/serializer/BigIntegerCodec.java
+++ b/src/main/java/com/alibaba/fastjson/serializer/BigIntegerCodec.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2017 Alibaba Group.
+ * Copyright 1999-2018 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@
import java.lang.reflect.Type;
import java.math.BigInteger;
+import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.parser.DefaultJSONParser;
import com.alibaba.fastjson.parser.JSONLexer;
import com.alibaba.fastjson.parser.JSONToken;
@@ -29,10 +30,16 @@
* @author wenshao[szujobs@hotmail.com]
*/
public class BigIntegerCodec implements ObjectSerializer, ObjectDeserializer {
+ private final static BigInteger LOW = BigInteger.valueOf(-9007199254740991L);
+ private final static BigInteger HIGH = BigInteger.valueOf(9007199254740991L);
public final static BigIntegerCodec instance = new BigIntegerCodec();
- public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
+ public void write(JSONSerializer serializer
+ , Object object
+ , Object fieldName
+ , Type fieldType, int features) throws IOException
+ {
SerializeWriter out = serializer.out;
if (object == null) {
@@ -41,7 +48,16 @@ public void write(JSONSerializer serializer, Object object, Object fieldName, Ty
}
BigInteger val = (BigInteger) object;
- out.write(val.toString());
+ String str = val.toString();
+ if (str.length() >= 16
+ && SerializerFeature.isEnabled(features, out.features, SerializerFeature.BrowserCompatible)
+ && (val.compareTo(LOW) < 0
+ || val.compareTo(HIGH) > 0))
+ {
+ out.writeString(str);
+ return;
+ }
+ out.write(str);
}
@SuppressWarnings("unchecked")
@@ -55,6 +71,11 @@ public static T deserialze(DefaultJSONParser parser) {
if (lexer.token() == JSONToken.LITERAL_INT) {
String val = lexer.numberString();
lexer.nextToken(JSONToken.COMMA);
+
+ if (val.length() > 65535) {
+ throw new JSONException("decimal overflow");
+ }
+
return (T) new BigInteger(val);
}
diff --git a/src/main/java/com/alibaba/fastjson/serializer/BooleanCodec.java b/src/main/java/com/alibaba/fastjson/serializer/BooleanCodec.java
old mode 100755
new mode 100644
index f9b732ba4a..c485172eb0
--- a/src/main/java/com/alibaba/fastjson/serializer/BooleanCodec.java
+++ b/src/main/java/com/alibaba/fastjson/serializer/BooleanCodec.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2017 Alibaba Group.
+ * Copyright 1999-2018 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/src/main/java/com/alibaba/fastjson/serializer/ByteBufferCodec.java b/src/main/java/com/alibaba/fastjson/serializer/ByteBufferCodec.java
new file mode 100644
index 0000000000..f58913bdc4
--- /dev/null
+++ b/src/main/java/com/alibaba/fastjson/serializer/ByteBufferCodec.java
@@ -0,0 +1,54 @@
+package com.alibaba.fastjson.serializer;
+
+import com.alibaba.fastjson.parser.DefaultJSONParser;
+import com.alibaba.fastjson.parser.JSONToken;
+import com.alibaba.fastjson.parser.deserializer.ObjectDeserializer;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.nio.ByteBuffer;
+
+public class ByteBufferCodec implements ObjectSerializer, ObjectDeserializer {
+ public final static ByteBufferCodec instance = new ByteBufferCodec();
+
+ @Override
+ public T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
+ ByteBufferBean bean = parser.parseObject(ByteBufferBean.class);
+ return (T) bean.byteBuffer();
+ }
+
+ @Override
+ public int getFastMatchToken() {
+ return JSONToken.LBRACKET;
+ }
+
+ @Override
+ public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
+ ByteBuffer byteBuf = (ByteBuffer) object;
+
+ byte[] array = byteBuf.array();
+
+ SerializeWriter out = serializer.out;
+ out.write('{');
+
+ out.writeFieldName("array");
+ out.writeByteArray(array);
+ out.writeFieldValue(',', "limit", byteBuf.limit());
+ out.writeFieldValue(',', "position", byteBuf.position());
+
+ out.write('}');
+ }
+
+ public static class ByteBufferBean {
+ public byte[] array;
+ public int limit;
+ public int position;
+
+ public ByteBuffer byteBuffer() {
+ ByteBuffer buf = ByteBuffer.wrap(array);
+ buf.limit(limit);
+ buf.position(position);
+ return buf;
+ }
+ }
+}
diff --git a/src/main/java/com/alibaba/fastjson/serializer/CalendarCodec.java b/src/main/java/com/alibaba/fastjson/serializer/CalendarCodec.java
old mode 100755
new mode 100644
index a29a1666a0..6ae90ceff2
--- a/src/main/java/com/alibaba/fastjson/serializer/CalendarCodec.java
+++ b/src/main/java/com/alibaba/fastjson/serializer/CalendarCodec.java
@@ -2,13 +2,17 @@
import java.io.IOException;
import java.lang.reflect.Type;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
+import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.DefaultJSONParser;
import com.alibaba.fastjson.parser.JSONLexer;
import com.alibaba.fastjson.parser.JSONToken;
+import com.alibaba.fastjson.parser.deserializer.ContextObjectDeserializer;
import com.alibaba.fastjson.parser.deserializer.ObjectDeserializer;
import com.alibaba.fastjson.util.IOUtils;
@@ -16,12 +20,33 @@
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
-public class CalendarCodec implements ObjectSerializer, ObjectDeserializer {
+public class CalendarCodec extends ContextObjectDeserializer implements ObjectSerializer, ObjectDeserializer, ContextObjectSerializer {
public final static CalendarCodec instance = new CalendarCodec();
private DatatypeFactory dateFactory;
+ public void write(JSONSerializer serializer, Object object, BeanContext context) throws IOException {
+ SerializeWriter out = serializer.out;
+ String format = context.getFormat();
+ Calendar calendar = (Calendar) object;
+
+ if (format.equals("unixtime")) {
+ long seconds = calendar.getTimeInMillis() / 1000L;
+ out.writeInt((int) seconds);
+ return;
+ }
+
+ DateFormat dateFormat = new SimpleDateFormat(format);
+ if (dateFormat == null) {
+ dateFormat = new SimpleDateFormat(JSON.DEFFAULT_DATE_FORMAT, serializer.locale);
+ }
+ dateFormat.setTimeZone(serializer.timeZone);
+ String text = dateFormat.format(calendar.getTime());
+ out.writeString(text);
+ }
+
+
public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features)
throws IOException {
SerializeWriter out = serializer.out;
@@ -82,13 +107,31 @@ public void write(JSONSerializer serializer, Object object, Object fieldName, Ty
out.write(buf);
- int timeZone = calendar.getTimeZone().getRawOffset() / (3600 * 1000);
- if (timeZone == 0) {
- out.append("Z");
- } else if (timeZone > 0) {
- out.append("+").append(String.format("%02d", timeZone)).append(":00");
+ float timeZoneF = calendar.getTimeZone().getOffset(calendar.getTimeInMillis()) / (3600.0f * 1000);
+ int timeZone = (int)timeZoneF;
+ if (timeZone == 0.0) {
+ out.write('Z');
} else {
- out.append("-").append(String.format("%02d", -timeZone)).append(":00");
+ if (timeZone > 9) {
+ out.write('+');
+ out.writeInt(timeZone);
+ } else if (timeZone > 0) {
+ out.write('+');
+ out.write('0');
+ out.writeInt(timeZone);
+ } else if (timeZone < -9) {
+ out.write('-');
+ out.writeInt(timeZone);
+ } else if (timeZone < 0) {
+ out.write('-');
+ out.write('0');
+ out.writeInt(-timeZone);
+ }
+ out.write(':');
+ // handles uneven timeZones 30 mins, 45 mins
+ // this would always be less than 60
+ int offSet = (int)((timeZoneF - timeZone) * 60);
+ out.append(String.format("%02d", offSet));
}
out.append(quote);
@@ -98,9 +141,14 @@ public void write(JSONSerializer serializer, Object object, Object fieldName, Ty
}
}
+ public T deserialze(DefaultJSONParser parser, Type clazz, Object fieldName) {
+ return deserialze(parser, clazz, fieldName, null, 0);
+ }
+
+ @Override
@SuppressWarnings("unchecked")
- public T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
- Object value = DateCodec.instance.deserialze(parser, type, fieldName);
+ public T deserialze(DefaultJSONParser parser, Type type, Object fieldName, String format, int features) {
+ Object value = DateCodec.instance.deserialze(parser, type, fieldName, format, features);
if (value instanceof Calendar) {
return (T) value;
diff --git a/src/main/java/com/alibaba/fastjson/serializer/CharArrayCodec.java b/src/main/java/com/alibaba/fastjson/serializer/CharArrayCodec.java
old mode 100755
new mode 100644
index afafcc1eab..d6a31e7786
--- a/src/main/java/com/alibaba/fastjson/serializer/CharArrayCodec.java
+++ b/src/main/java/com/alibaba/fastjson/serializer/CharArrayCodec.java
@@ -2,7 +2,6 @@
import java.lang.reflect.Type;
import java.util.Collection;
-import java.util.List;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
diff --git a/src/main/java/com/alibaba/fastjson/serializer/CharacterCodec.java b/src/main/java/com/alibaba/fastjson/serializer/CharacterCodec.java
old mode 100755
new mode 100644
index 24aaab8140..6f82c4f2ca
--- a/src/main/java/com/alibaba/fastjson/serializer/CharacterCodec.java
+++ b/src/main/java/com/alibaba/fastjson/serializer/CharacterCodec.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2017 Alibaba Group.
+ * Copyright 1999-2018 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/src/main/java/com/alibaba/fastjson/serializer/ClobSeriliazer.java b/src/main/java/com/alibaba/fastjson/serializer/ClobSerializer.java
old mode 100755
new mode 100644
similarity index 91%
rename from src/main/java/com/alibaba/fastjson/serializer/ClobSeriliazer.java
rename to src/main/java/com/alibaba/fastjson/serializer/ClobSerializer.java
index 0605dd6600..078b89c6bb
--- a/src/main/java/com/alibaba/fastjson/serializer/ClobSeriliazer.java
+++ b/src/main/java/com/alibaba/fastjson/serializer/ClobSerializer.java
@@ -8,9 +8,9 @@
import com.alibaba.fastjson.JSONException;
-public class ClobSeriliazer implements ObjectSerializer {
+public class ClobSerializer implements ObjectSerializer {
- public final static ClobSeriliazer instance = new ClobSeriliazer();
+ public final static ClobSerializer instance = new ClobSerializer();
public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
try {
diff --git a/src/main/java/com/alibaba/fastjson/serializer/CollectionCodec.java b/src/main/java/com/alibaba/fastjson/serializer/CollectionCodec.java
old mode 100755
new mode 100644
index 5e88f7140f..95d5c3e3eb
--- a/src/main/java/com/alibaba/fastjson/serializer/CollectionCodec.java
+++ b/src/main/java/com/alibaba/fastjson/serializer/CollectionCodec.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2017 Alibaba Group.
+ * Copyright 1999-2018 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,12 +16,9 @@
package com.alibaba.fastjson.serializer;
import java.io.IOException;
-import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
-import java.lang.reflect.WildcardType;
import java.util.Collection;
import java.util.HashSet;
-import java.util.Map;
import java.util.TreeSet;
import com.alibaba.fastjson.JSONArray;
@@ -58,7 +55,7 @@ public void write(JSONSerializer serializer, Object object, Object fieldName, Ty
serializer.setContext(context, object, fieldName, 0);
if (out.isEnabled(SerializerFeature.WriteClassName)) {
- if (HashSet.class == collection.getClass()) {
+ if (HashSet.class.isAssignableFrom(collection.getClass())) {
out.append("Set");
} else if (TreeSet.class == collection.getClass()) {
out.append("TreeSet");
@@ -123,7 +120,13 @@ public T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
return (T) array;
}
- Collection list = TypeUtils.createCollection(type);
+ Collection list;
+ if (parser.lexer.token() == JSONToken.SET) {
+ parser.lexer.nextToken();
+ list = TypeUtils.createSet(type);
+ } else {
+ list = TypeUtils.createCollection(type);
+ }
Type itemType = TypeUtils.getCollectionItemType(type);
parser.parseArray(itemType, list, fieldName);
diff --git a/src/main/java/com/alibaba/fastjson/serializer/DateCodec.java b/src/main/java/com/alibaba/fastjson/serializer/DateCodec.java
index f1e5ee2570..31df03ae4f 100644
--- a/src/main/java/com/alibaba/fastjson/serializer/DateCodec.java
+++ b/src/main/java/com/alibaba/fastjson/serializer/DateCodec.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2017 Alibaba Group.
+ * Copyright 1999-2018 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
import java.io.IOException;
import java.lang.reflect.Type;
+import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
@@ -48,6 +49,45 @@ public void write(JSONSerializer serializer, Object object, Object fieldName, Ty
out.writeNull();
return;
}
+
+ Class> clazz = object.getClass();
+ if (clazz == java.sql.Date.class && !out.isEnabled(SerializerFeature.WriteDateUseDateFormat)) {
+ long millis = ((java.sql.Date) object).getTime();
+ TimeZone timeZone = serializer.timeZone;
+ int offset = timeZone.getOffset(millis);
+ //
+ if ((millis + offset) % (24 * 1000 * 3600) == 0
+ && !SerializerFeature.isEnabled(out.features, features, SerializerFeature.WriteClassName)) {
+ out.writeString(object.toString());
+ return;
+ }
+ }
+
+ if (clazz == java.sql.Time.class) {
+ long millis = ((java.sql.Time) object).getTime();
+ if ("unixtime".equals(serializer.getDateFormatPattern())) {
+ long seconds = millis / 1000;
+ out.writeLong(seconds);
+ return;
+ }
+
+ if ("millis".equals(serializer.getDateFormatPattern())) {
+ long seconds = millis;
+ out.writeLong(millis);
+ return;
+ }
+
+ if (millis < 24L * 60L * 60L * 1000L) {
+ out.writeString(object.toString());
+ return;
+ }
+ }
+
+ int nanos = 0;
+ if (clazz == java.sql.Timestamp.class) {
+ java.sql.Timestamp ts = (java.sql.Timestamp) object;
+ nanos = ts.getNanos();
+ }
Date date;
if (object instanceof Date) {
@@ -55,11 +95,29 @@ public void write(JSONSerializer serializer, Object object, Object fieldName, Ty
} else {
date = TypeUtils.castToDate(object);
}
-
+
+ if ("unixtime".equals(serializer.getDateFormatPattern())) {
+ long seconds = date.getTime() / 1000;
+ out.writeLong(seconds);
+ return;
+ }
+
+ if ("millis".equals(serializer.getDateFormatPattern())) {
+ long millis = date.getTime();
+ out.writeLong(millis);
+ return;
+ }
+
if (out.isEnabled(SerializerFeature.WriteDateUseDateFormat)) {
DateFormat format = serializer.getDateFormat();
if (format == null) {
- format = new SimpleDateFormat(JSON.DEFFAULT_DATE_FORMAT, serializer.locale);
+ // 如果是通过FastJsonConfig进行设置,优先从FastJsonConfig获取
+ String dateFormatPattern = serializer.getFastJsonConfigDateFormatPattern();
+ if(dateFormatPattern == null) {
+ dateFormatPattern = JSON.DEFFAULT_DATE_FORMAT;
+ }
+
+ format = new SimpleDateFormat(dateFormatPattern, serializer.locale);
format.setTimeZone(serializer.timeZone);
}
String text = format.format(date);
@@ -68,15 +126,15 @@ public void write(JSONSerializer serializer, Object object, Object fieldName, Ty
}
if (out.isEnabled(SerializerFeature.WriteClassName)) {
- if (object.getClass() != fieldType) {
- if (object.getClass() == java.util.Date.class) {
+ if (clazz != fieldType) {
+ if (clazz == java.util.Date.class) {
out.write("new Date(");
out.writeLong(((Date) object).getTime());
out.write(')');
} else {
out.write('{');
out.writeFieldName(JSON.DEFAULT_TYPE_KEY);
- serializer.write(object.getClass().getName());
+ serializer.write(clazz.getName());
out.writeFieldValue(',', "val", ((Date) object).getTime());
out.write('}');
}
@@ -101,7 +159,16 @@ public void write(JSONSerializer serializer, Object object, Object fieldName, Ty
int millis = calendar.get(Calendar.MILLISECOND);
char[] buf;
- if (millis != 0) {
+ if (nanos > 0) {
+ buf = "0000-00-00 00:00:00.000000000".toCharArray();
+ IOUtils.getChars(nanos, 29, buf);
+ IOUtils.getChars(second, 19, buf);
+ IOUtils.getChars(minute, 16, buf);
+ IOUtils.getChars(hour, 13, buf);
+ IOUtils.getChars(day, 10, buf);
+ IOUtils.getChars(month, 7, buf);
+ IOUtils.getChars(year, 4, buf);
+ } else if (millis != 0) {
buf = "0000-00-00T00:00:00.000".toCharArray();
IOUtils.getChars(millis, 23, buf);
IOUtils.getChars(second, 19, buf);
@@ -127,11 +194,26 @@ public void write(JSONSerializer serializer, Object object, Object fieldName, Ty
IOUtils.getChars(year, 4, buf);
}
}
-
+
+
+ if (nanos > 0) { // java.sql.Timestamp
+ int i = 0;
+ for (; i < 9; ++i) {
+ int off = buf.length - i - 1;
+ if (buf[off] != '0') {
+ break;
+ }
+ }
+ out.write(buf, 0, buf.length - i);
+ out.write(quote);
+ return;
+ }
+
out.write(buf);
-
- int timeZone = calendar.getTimeZone().getRawOffset()/(3600*1000);
- if (timeZone == 0) {
+
+ float timeZoneF = calendar.getTimeZone().getOffset(calendar.getTimeInMillis()) / (3600.0f * 1000);
+ int timeZone = (int)timeZoneF;
+ if (timeZone == 0.0) {
out.write('Z');
} else {
if (timeZone > 9) {
@@ -143,14 +225,17 @@ public void write(JSONSerializer serializer, Object object, Object fieldName, Ty
out.writeInt(timeZone);
} else if (timeZone < -9) {
out.write('-');
- out.writeInt(timeZone);
+ out.writeInt(-timeZone);
} else if (timeZone < 0) {
out.write('-');
out.write('0');
- out.writeInt(timeZone);
+ out.writeInt(-timeZone);
}
-
- out.append(":00");
+ out.write(':');
+ // handles uneven timeZones 30 mins, 45 mins
+ // this would always be less than 60
+ int offSet = (int)(Math.abs(timeZoneF - timeZone) * 60);
+ out.append(String.format("%02d", offSet));
}
out.write(quote);
@@ -168,6 +253,8 @@ public T cast(DefaultJSONParser parser, Type clazz, Object fieldName, Object
if (val instanceof java.util.Date) {
return (T) val;
+ } else if (val instanceof BigDecimal) {
+ return (T) new java.util.Date(TypeUtils.longValue((BigDecimal) val));
} else if (val instanceof Number) {
return (T) new java.util.Date(((Number) val).longValue());
} else if (val instanceof String) {
@@ -176,6 +263,10 @@ public T cast(DefaultJSONParser parser, Type clazz, Object fieldName, Object
return null;
}
+ if (strVal.length() == 23 && strVal.endsWith(" 000")) {
+ strVal = strVal.substring(0, 19);
+ }
+
{
JSONScanner dateLexer = new JSONScanner(strVal);
try {
@@ -192,9 +283,13 @@ public T cast(DefaultJSONParser parser, Type clazz, Object fieldName, Object
dateLexer.close();
}
}
-
- if (strVal.length() == parser.getDateFomartPattern().length()
- || (strVal.length() == 22 && parser.getDateFomartPattern().equals("yyyyMMddHHmmssSSSZ"))) {
+
+ String dateFomartPattern = parser.getDateFomartPattern();
+ boolean formatMatch = strVal.length() == dateFomartPattern.length()
+ || (strVal.length() == 22 && dateFomartPattern.equals("yyyyMMddHHmmssSSSZ"))
+ || (strVal.indexOf('T') != -1 && dateFomartPattern.contains("'T'") && strVal.length() + 2 == dateFomartPattern.length())
+ ;
+ if (formatMatch) {
DateFormat dateFormat = parser.getDateFormat();
try {
return (T) dateFormat.parse(strVal);
diff --git a/src/main/java/com/alibaba/fastjson/serializer/DoubleSerializer.java b/src/main/java/com/alibaba/fastjson/serializer/DoubleSerializer.java
old mode 100755
new mode 100644
index 8f3ac0836d..d7c05699e1
--- a/src/main/java/com/alibaba/fastjson/serializer/DoubleSerializer.java
+++ b/src/main/java/com/alibaba/fastjson/serializer/DoubleSerializer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2017 Alibaba Group.
+ * Copyright 1999-2018 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/src/main/java/com/alibaba/fastjson/serializer/EnumSerializer.java b/src/main/java/com/alibaba/fastjson/serializer/EnumSerializer.java
old mode 100755
new mode 100644
index 2a055144af..08eb7e2910
--- a/src/main/java/com/alibaba/fastjson/serializer/EnumSerializer.java
+++ b/src/main/java/com/alibaba/fastjson/serializer/EnumSerializer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2017 Alibaba Group.
+ * Copyright 1999-2018 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,18 +15,46 @@
*/
package com.alibaba.fastjson.serializer;
+import com.alibaba.fastjson.JSONException;
+
import java.io.IOException;
-import java.lang.reflect.Type;
+import java.lang.reflect.*;
/**
* @author wenshao[szujobs@hotmail.com]
*/
public class EnumSerializer implements ObjectSerializer {
+ private final Member member;
+
+ public EnumSerializer() {
+ this.member = null;
+ }
+
+ public EnumSerializer(Member member) {
+ this.member = member;
+ }
+
public final static EnumSerializer instance = new EnumSerializer();
public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
- SerializeWriter out = serializer.out;
- out.writeEnum((Enum>) object);
+ if (member == null) {
+ SerializeWriter out = serializer.out;
+ out.writeEnum((Enum>) object);
+ return;
+ }
+
+ Object fieldValue = null;
+ try {
+ if (member instanceof Field) {
+ fieldValue = ((Field) member).get(object);
+ } else {
+ fieldValue = ((Method) member).invoke(object);
+ }
+ } catch (Exception e) {
+ throw new JSONException("getEnumValue error", e);
+ }
+
+ serializer.write(fieldValue);
}
}
diff --git a/src/main/java/com/alibaba/fastjson/serializer/EnumerationSerializer.java b/src/main/java/com/alibaba/fastjson/serializer/EnumerationSerializer.java
old mode 100755
new mode 100644
diff --git a/src/main/java/com/alibaba/fastjson/serializer/FieldSerializer.java b/src/main/java/com/alibaba/fastjson/serializer/FieldSerializer.java
old mode 100755
new mode 100644
index acfebe8ab7..2a438cfdd2
--- a/src/main/java/com/alibaba/fastjson/serializer/FieldSerializer.java
+++ b/src/main/java/com/alibaba/fastjson/serializer/FieldSerializer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2017 Alibaba Group.
+ * Copyright 1999-2018 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,7 +25,6 @@
import java.lang.reflect.InvocationTargetException;
import java.text.SimpleDateFormat;
import java.util.Collection;
-import java.util.Date;
/**
* @author wenshao[szujobs@hotmail.com]
@@ -50,6 +49,7 @@ public class FieldSerializer implements Comparable {
protected boolean serializeUsing = false;
protected boolean persistenceXToMany = false; // OneToMany or ManyToMany
+ protected boolean browserCompatible;
private RuntimeSerializerInfo runtimeInfo;
@@ -57,16 +57,21 @@ public FieldSerializer(Class> beanType, FieldInfo fieldInfo) {
this.fieldInfo = fieldInfo;
this.fieldContext = new BeanContext(beanType, fieldInfo);
- if (beanType != null && fieldInfo.isEnum) {
+ if (beanType != null) {
JSONType jsonType = TypeUtils.getAnnotation(beanType,JSONType.class);
if (jsonType != null) {
for (SerializerFeature feature : jsonType.serialzeFeatures()) {
if (feature == SerializerFeature.WriteEnumUsingToString) {
writeEnumUsingToString = true;
- }else if(feature == SerializerFeature.WriteEnumUsingName){
+ } else if(feature == SerializerFeature.WriteEnumUsingName){
writeEnumUsingName = true;
- }else if(feature == SerializerFeature.DisableCircularReferenceDetect){
+ } else if(feature == SerializerFeature.DisableCircularReferenceDetect){
disableCircularReferenceDetect = true;
+ } else if(feature == SerializerFeature.BrowserCompatible){
+ features |= SerializerFeature.BrowserCompatible.mask;
+ browserCompatible = true;
+ } else if (feature == SerializerFeature.WriteMapNullValue) {
+ features |= SerializerFeature.WriteMapNullValue.mask;
}
}
}
@@ -95,14 +100,16 @@ public FieldSerializer(Class> beanType, FieldInfo fieldInfo) {
for (SerializerFeature feature : annotation.serialzeFeatures()) {
if (feature == SerializerFeature.WriteEnumUsingToString) {
writeEnumUsingToString = true;
- }else if(feature == SerializerFeature.WriteEnumUsingName){
+ } else if(feature == SerializerFeature.WriteEnumUsingName){
writeEnumUsingName = true;
- }else if(feature == SerializerFeature.DisableCircularReferenceDetect){
+ } else if(feature == SerializerFeature.DisableCircularReferenceDetect){
disableCircularReferenceDetect = true;
+ } else if(feature == SerializerFeature.BrowserCompatible){
+ browserCompatible = true;
}
}
- features = SerializerFeature.of(annotation.serialzeFeatures());
+ features |= SerializerFeature.of(annotation.serialzeFeatures());
}
this.writeNull = writeNull;
@@ -115,7 +122,8 @@ public void writePrefix(JSONSerializer serializer) throws IOException {
SerializeWriter out = serializer.out;
if (out.quoteFieldNames) {
- if (out.useSingleQuotes) {
+ boolean useSingleQuotes = SerializerFeature.isEnabled(out.features, fieldInfo.serialzeFeatures, SerializerFeature.UseSingleQuotes);
+ if (useSingleQuotes) {
if (single_quoted_fieldPrefix == null) {
single_quoted_fieldPrefix = '\'' + fieldInfo.name + "\':";
}
@@ -142,8 +150,8 @@ public Object getPropertyValueDirect(Object object) throws InvocationTargetExcep
public Object getPropertyValue(Object object) throws InvocationTargetException, IllegalAccessException {
Object propertyValue = fieldInfo.get(object);
if (format != null && propertyValue != null) {
- if (fieldInfo.fieldClass == Date.class) {
- SimpleDateFormat dateFormat = new SimpleDateFormat(format);
+ if (fieldInfo.fieldClass == java.util.Date.class || fieldInfo.fieldClass == java.sql.Date.class) {
+ SimpleDateFormat dateFormat = new SimpleDateFormat(format, JSON.defaultLocale);
dateFormat.setTimeZone(JSON.defaultTimeZone);
return dateFormat.format(propertyValue);
}
@@ -162,6 +170,21 @@ public void writeValue(JSONSerializer serializer, Object propertyValue) throws E
Class> runtimeFieldClass;
if (propertyValue == null) {
runtimeFieldClass = this.fieldInfo.fieldClass;
+ if (runtimeFieldClass == byte.class) {
+ runtimeFieldClass = Byte.class;
+ } else if (runtimeFieldClass == short.class) {
+ runtimeFieldClass = Short.class;
+ } else if (runtimeFieldClass == int.class) {
+ runtimeFieldClass = Integer.class;
+ } else if (runtimeFieldClass == long.class) {
+ runtimeFieldClass = Long.class;
+ } else if (runtimeFieldClass == float.class) {
+ runtimeFieldClass = Float.class;
+ } else if (runtimeFieldClass == double.class) {
+ runtimeFieldClass = Double.class;
+ } else if (runtimeFieldClass == boolean.class) {
+ runtimeFieldClass = Boolean.class;
+ }
} else {
runtimeFieldClass = propertyValue.getClass();
}
@@ -191,8 +214,10 @@ public void writeValue(JSONSerializer serializer, Object propertyValue) throws E
final RuntimeSerializerInfo runtimeInfo = this.runtimeInfo;
- final int fieldFeatures = disableCircularReferenceDetect?
- (fieldInfo.serialzeFeatures|SerializerFeature.DisableCircularReferenceDetect.getMask()):fieldInfo.serialzeFeatures;
+ final int fieldFeatures
+ = (disableCircularReferenceDetect
+ ? (fieldInfo.serialzeFeatures | SerializerFeature.DisableCircularReferenceDetect.mask)
+ : fieldInfo.serialzeFeatures) | features;
if (propertyValue == null) {
SerializeWriter out = serializer.out;
@@ -214,7 +239,8 @@ public void writeValue(JSONSerializer serializer, Object propertyValue) throws E
} else if (Boolean.class == runtimeFieldClass) {
out.writeNull(features, SerializerFeature.WriteNullBooleanAsFalse.mask);
return;
- } else if (Collection.class.isAssignableFrom(runtimeFieldClass)) {
+ } else if (Collection.class.isAssignableFrom(runtimeFieldClass)
+ || runtimeFieldClass.isArray()) {
out.writeNull(features, SerializerFeature.WriteNullListAsEmpty.mask);
return;
}
@@ -276,11 +302,19 @@ public void writeValue(JSONSerializer serializer, Object propertyValue) throws E
if ((features & SerializerFeature.WriteClassName.mask) != 0
&& valueClass != fieldInfo.fieldClass
- && JavaBeanSerializer.class.isInstance(valueSerializer)) {
+ && valueSerializer instanceof JavaBeanSerializer) {
((JavaBeanSerializer) valueSerializer).write(serializer, propertyValue, fieldInfo.name, fieldInfo.fieldType, fieldFeatures, false);
return;
}
+ if (browserCompatible && (fieldInfo.fieldClass == long.class || fieldInfo.fieldClass == Long.class)) {
+ long value = (Long) propertyValue;
+ if (value > 9007199254740991L || value < -9007199254740991L) {
+ serializer.getWriter().writeString(Long.toString(value));
+ return;
+ }
+ }
+
valueSerializer.write(serializer, propertyValue, fieldInfo.name, fieldInfo.fieldType, fieldFeatures);
}
diff --git a/src/main/java/com/alibaba/fastjson/serializer/FloatCodec.java b/src/main/java/com/alibaba/fastjson/serializer/FloatCodec.java
old mode 100755
new mode 100644
index 5fad6fc8d9..5f80019b13
--- a/src/main/java/com/alibaba/fastjson/serializer/FloatCodec.java
+++ b/src/main/java/com/alibaba/fastjson/serializer/FloatCodec.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2017 Alibaba Group.
+ * Copyright 1999-2018 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/src/main/java/com/alibaba/fastjson/serializer/GuavaCodec.java b/src/main/java/com/alibaba/fastjson/serializer/GuavaCodec.java
index 45c21f0492..4917886ebc 100644
--- a/src/main/java/com/alibaba/fastjson/serializer/GuavaCodec.java
+++ b/src/main/java/com/alibaba/fastjson/serializer/GuavaCodec.java
@@ -1,16 +1,22 @@
package com.alibaba.fastjson.serializer;
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.parser.DefaultJSONParser;
+import com.alibaba.fastjson.parser.deserializer.ObjectDeserializer;
+import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import java.io.IOException;
+import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
+import java.util.List;
import java.util.Map;
/**
* Created by wenshao on 15/01/2017.
*/
-public class GuavaCodec implements ObjectSerializer {
+public class GuavaCodec implements ObjectSerializer, ObjectDeserializer {
public static GuavaCodec instance = new GuavaCodec();
public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
@@ -20,4 +26,31 @@ public void write(JSONSerializer serializer, Object object, Object fieldName, Ty
serializer.write(multimap.asMap());
}
}
+
+ public T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
+ Type rawType = type;
+ if (type instanceof ParameterizedType) {
+ rawType = ((ParameterizedType) type).getRawType();
+ }
+
+ if (rawType == ArrayListMultimap.class) {
+ ArrayListMultimap multimap = ArrayListMultimap.create();
+ JSONObject object = parser.parseObject();
+ for (Map.Entry entry : object.entrySet()) {
+ Object value = entry.getValue();
+ if (value instanceof Collection) {
+ multimap.putAll(entry.getKey(), (List) value);
+ } else {
+ multimap.put(entry.getKey(), value);
+ }
+ }
+
+ return (T) multimap;
+ }
+ return null;
+ }
+
+ public int getFastMatchToken() {
+ return 0;
+ }
}
diff --git a/src/main/java/com/alibaba/fastjson/serializer/IntegerCodec.java b/src/main/java/com/alibaba/fastjson/serializer/IntegerCodec.java
old mode 100755
new mode 100644
index 158d5094e5..c734600b81
--- a/src/main/java/com/alibaba/fastjson/serializer/IntegerCodec.java
+++ b/src/main/java/com/alibaba/fastjson/serializer/IntegerCodec.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2017 Alibaba Group.
+ * Copyright 1999-2018 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,9 +18,7 @@
import java.io.IOException;
import java.lang.reflect.Type;
import java.math.BigDecimal;
-import java.util.Iterator;
import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicLong;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
@@ -82,9 +80,9 @@ public T deserialze(DefaultJSONParser parser, Type clazz, Object fieldName)
lexer.nextToken(JSONToken.COMMA);
intObj = Integer.valueOf(val);
} else if (token == JSONToken.LITERAL_FLOAT) {
- BigDecimal decimalValue = lexer.decimalValue();
+ BigDecimal number = lexer.decimalValue();
+ intObj = TypeUtils.intValue(number);
lexer.nextToken(JSONToken.COMMA);
- intObj = Integer.valueOf(decimalValue.intValue());
} else {
if (token == JSONToken.LBRACE) {
JSONObject jsonObject = new JSONObject(true);
@@ -96,7 +94,11 @@ public T deserialze(DefaultJSONParser parser, Type clazz, Object fieldName)
}
}
} catch (Exception ex) {
- throw new JSONException("parseInt error, field : " + fieldName, ex);
+ String message = "parseInt error";
+ if (fieldName != null) {
+ message += (", field : " + fieldName);
+ }
+ throw new JSONException(message, ex);
}
diff --git a/src/main/java/com/alibaba/fastjson/serializer/JSONAwareSerializer.java b/src/main/java/com/alibaba/fastjson/serializer/JSONAwareSerializer.java
old mode 100755
new mode 100644
index 6192ccca5d..64c9e1e005
--- a/src/main/java/com/alibaba/fastjson/serializer/JSONAwareSerializer.java
+++ b/src/main/java/com/alibaba/fastjson/serializer/JSONAwareSerializer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2017 Alibaba Group.
+ * Copyright 1999-2018 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/src/main/java/com/alibaba/fastjson/serializer/JSONLibDataFormatSerializer.java b/src/main/java/com/alibaba/fastjson/serializer/JSONLibDataFormatSerializer.java
old mode 100755
new mode 100644
diff --git a/src/main/java/com/alibaba/fastjson/serializer/JSONObjectCodec.java b/src/main/java/com/alibaba/fastjson/serializer/JSONObjectCodec.java
new file mode 100644
index 0000000000..0736cbefa4
--- /dev/null
+++ b/src/main/java/com/alibaba/fastjson/serializer/JSONObjectCodec.java
@@ -0,0 +1,29 @@
+package com.alibaba.fastjson.serializer;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Type;
+
+public class JSONObjectCodec implements ObjectSerializer {
+ public final static JSONObjectCodec instance = new JSONObjectCodec();
+
+ public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features)
+ throws IOException {
+ SerializeWriter out = serializer.out;
+ MapSerializer mapSerializer = MapSerializer.instance;
+
+ try {
+ Field mapField = object.getClass().getDeclaredField("map");
+ if (Modifier.isPrivate(mapField.getModifiers())) {
+ mapField.setAccessible(true);
+ }
+
+ Object map = mapField.get(object);
+ mapSerializer.write(serializer, map, fieldName, fieldType, features);
+
+ } catch (Exception e) {
+ out.writeNull();
+ }
+ }
+}
diff --git a/src/main/java/com/alibaba/fastjson/serializer/JSONSerializable.java b/src/main/java/com/alibaba/fastjson/serializer/JSONSerializable.java
index 401217fb59..a19adfebf4 100644
--- a/src/main/java/com/alibaba/fastjson/serializer/JSONSerializable.java
+++ b/src/main/java/com/alibaba/fastjson/serializer/JSONSerializable.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2017 Alibaba Group.
+ * Copyright 1999-2018 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/src/main/java/com/alibaba/fastjson/serializer/JSONSerializableSerializer.java b/src/main/java/com/alibaba/fastjson/serializer/JSONSerializableSerializer.java
index 5967125675..88dc1354b6 100644
--- a/src/main/java/com/alibaba/fastjson/serializer/JSONSerializableSerializer.java
+++ b/src/main/java/com/alibaba/fastjson/serializer/JSONSerializableSerializer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2017 Alibaba Group.
+ * Copyright 1999-2018 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,6 +27,10 @@ public class JSONSerializableSerializer implements ObjectSerializer {
public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
JSONSerializable jsonSerializable = ((JSONSerializable) object);
+ if (jsonSerializable == null) {
+ serializer.writeNull();
+ return;
+ }
jsonSerializable.write(serializer, fieldName, fieldType, features);
}
}
diff --git a/src/main/java/com/alibaba/fastjson/serializer/JSONSerializer.java b/src/main/java/com/alibaba/fastjson/serializer/JSONSerializer.java
old mode 100755
new mode 100644
index 756bc8d99f..8c5ce51c0a
--- a/src/main/java/com/alibaba/fastjson/serializer/JSONSerializer.java
+++ b/src/main/java/com/alibaba/fastjson/serializer/JSONSerializer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2017 Alibaba Group.
+ * Copyright 1999-2018 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,10 +21,7 @@
import java.lang.reflect.Type;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.IdentityHashMap;
-import java.util.Locale;
-import java.util.TimeZone;
+import java.util.*;
import java.util.zip.GZIPOutputStream;
import com.alibaba.fastjson.JSON;
@@ -42,9 +39,17 @@ public class JSONSerializer extends SerializeFilterable {
private int indentCount = 0;
private String indent = "\t";
+ /**
+ * #1868 为了区分全局配置(FastJsonConfig)的日期格式配置以及toJSONString传入的日期格式配置
+ * 建议使用以下调整:
+ * 1. dateFormatPattern、dateFormat只作为toJSONString传入配置使用;
+ * 2. 新增fastJsonConfigDateFormatPattern,用于存储通过(FastJsonConfig)配置的日期格式
+ */
private String dateFormatPattern;
private DateFormat dateFormat;
+ private String fastJsonConfigDateFormatPattern;
+
protected IdentityHashMap references = null;
protected SerialContext context;
@@ -78,14 +83,20 @@ public String getDateFormatPattern() {
public DateFormat getDateFormat() {
if (dateFormat == null) {
if (dateFormatPattern != null) {
- dateFormat = new SimpleDateFormat(dateFormatPattern, locale);
- dateFormat.setTimeZone(timeZone);
+ dateFormat = this.generateDateFormat( dateFormatPattern );
}
}
return dateFormat;
}
+ private DateFormat generateDateFormat(String dateFormatPattern) {
+ DateFormat dateFormat = new SimpleDateFormat(dateFormatPattern, locale);
+ dateFormat.setTimeZone(timeZone);
+
+ return dateFormat;
+ }
+
public void setDateFormat(DateFormat dateFormat) {
this.dateFormat = dateFormat;
if (dateFormatPattern != null) {
@@ -100,6 +111,19 @@ public void setDateFormat(String dateFormat) {
}
}
+ /**
+ * Set global date format pattern in FastJsonConfig
+ *
+ * @param dateFormatPattern global date format pattern
+ */
+ public void setFastJsonConfigDateFormatPattern(String dateFormatPattern) {
+ this.fastJsonConfigDateFormatPattern = dateFormatPattern;
+ }
+
+ public String getFastJsonConfigDateFormatPattern() {
+ return this.fastJsonConfigDateFormatPattern;
+ }
+
public SerialContext getContext() {
return context;
}
@@ -151,6 +175,10 @@ public boolean containsReference(Object value) {
return false;
}
+ if (value == Collections.emptyMap()) {
+ return false;
+ }
+
Object fieldName = refContext.fieldName;
return fieldName == null || fieldName instanceof Integer || fieldName instanceof String;
@@ -186,7 +214,8 @@ public void writeReference(Object object) {
out.write("{\"$ref\":\"$\"}");
} else {
out.write("{\"$ref\":\"");
- out.write(references.get(object).toString());
+ String path = references.get(object).toString();
+ out.write(path);
out.write("\"}");
}
}
@@ -204,6 +233,11 @@ public boolean hasNameFilters(SerializeFilterable filterable) {
|| (filterable.nameFilters != null && filterable.nameFilters.size() > 0);
}
+ public boolean hasPropertyFilters(SerializeFilterable filterable) {
+ return (propertyFilters != null && propertyFilters.size() > 0) //
+ || (filterable.propertyFilters != null && filterable.propertyFilters.size() > 0);
+ }
+
public int getIndentCount() {
return indentCount;
}
@@ -281,6 +315,25 @@ public final void write(Object object) {
}
}
+ /**
+ * @since 1.2.57
+ *
+ */
+ public final void writeAs(Object object, Class type) {
+ if (object == null) {
+ out.writeNull();
+ return;
+ }
+
+ ObjectSerializer writer = getObjectWriter(type);
+
+ try {
+ writer.write(this, object, null, null, 0);
+ } catch (IOException e) {
+ throw new JSONException(e.getMessage(), e);
+ }
+ }
+
public final void writeWithFieldName(Object object, Object fieldName) {
writeWithFieldName(object, fieldName, null, 0);
}
@@ -312,10 +365,31 @@ public final void writeWithFieldName(Object object, Object fieldName, Type field
public final void writeWithFormat(Object object, String format) {
if (object instanceof Date) {
+ if ("unixtime".equals(format)) {
+ long seconds = ((Date) object).getTime() / 1000L;
+ out.writeInt((int) seconds);
+ return;
+ }
+
+ if ("millis".equals(format)) {
+ out.writeLong(((Date) object).getTime());
+ return;
+ }
+
DateFormat dateFormat = this.getDateFormat();
if (dateFormat == null) {
- dateFormat = new SimpleDateFormat(format, locale);
- dateFormat.setTimeZone(timeZone);
+ if (format != null) {
+ try {
+ dateFormat = this.generateDateFormat(format);
+ } catch (IllegalArgumentException e) {
+ String format2 = format.replaceAll("T", "'T'");
+ dateFormat = this.generateDateFormat(format2);
+ }
+ } else if (fastJsonConfigDateFormatPattern != null) {
+ dateFormat = this.generateDateFormat(fastJsonConfigDateFormatPattern);
+ } else {
+ dateFormat = this.generateDateFormat(JSON.DEFFAULT_DATE_FORMAT);
+ }
}
String text = dateFormat.format((Date) object);
out.writeString(text);
@@ -348,6 +422,21 @@ public final void writeWithFormat(Object object, String format) {
}
return;
}
+
+ if (object instanceof Collection) {
+ Collection collection = (Collection) object;
+ Iterator iterator = collection.iterator();
+ out.write('[');
+ for (int i = 0; i < collection.size(); i++) {
+ Object item = iterator.next();
+ if (i != 0) {
+ out.write(',');
+ }
+ writeWithFormat(item, format);
+ }
+ out.write(']');
+ return;
+ }
write(object);
}
diff --git a/src/main/java/com/alibaba/fastjson/serializer/JSONSerializerMap.java b/src/main/java/com/alibaba/fastjson/serializer/JSONSerializerMap.java
old mode 100755
new mode 100644
diff --git a/src/main/java/com/alibaba/fastjson/serializer/JavaBeanSerializer.java b/src/main/java/com/alibaba/fastjson/serializer/JavaBeanSerializer.java
index 3708369998..fcd713d7f9 100644
--- a/src/main/java/com/alibaba/fastjson/serializer/JavaBeanSerializer.java
+++ b/src/main/java/com/alibaba/fastjson/serializer/JavaBeanSerializer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2017 Alibaba Group.
+ * Copyright 1999-2018 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,12 +20,22 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.PropertyNamingStrategy;
import com.alibaba.fastjson.annotation.JSONField;
+import com.alibaba.fastjson.annotation.JSONType;
import com.alibaba.fastjson.util.FieldInfo;
import com.alibaba.fastjson.util.TypeUtils;
@@ -37,7 +47,7 @@ public class JavaBeanSerializer extends SerializeFilterable implements ObjectSer
protected final FieldSerializer[] getters;
protected final FieldSerializer[] sortedGetters;
- protected SerializeBeanInfo beanInfo;
+ protected final SerializeBeanInfo beanInfo;
private transient volatile long[] hashArray;
private transient volatile short[] hashArrayMapping;
@@ -59,6 +69,17 @@ static Map createAliasMap(String... aliasList) {
return aliasMap;
}
+ public JSONType getJSONType() {
+ return beanInfo.jsonType;
+ }
+
+ /**
+ * @since 1.2.42
+ */
+ public Class> getType() {
+ return beanInfo.beanType;
+ }
+
public JavaBeanSerializer(Class> beanType, Map