diff --git a/src/main/java/com/alibaba/fastjson/JSON.java b/src/main/java/com/alibaba/fastjson/JSON.java index 1ac7dd8e34..132c9673fe 100755 --- a/src/main/java/com/alibaba/fastjson/JSON.java +++ b/src/main/java/com/alibaba/fastjson/JSON.java @@ -1092,7 +1092,7 @@ public static Object toJSON(Object javaObject, SerializeConfig config) { try { Map values = javaBeanSerializer.getFieldValuesMap(javaObject); for (Map.Entry entry : values.entrySet()) { - json.put(entry.getKey(), toJSON(entry.getValue())); + json.put(entry.getKey(), toJSON(entry.getValue(), config)); } } catch (Exception e) { throw new JSONException("toJSON error", e); diff --git a/src/main/java/com/alibaba/fastjson/annotation/JSONField.java b/src/main/java/com/alibaba/fastjson/annotation/JSONField.java index 9907c9ea86..e859d76924 100755 --- 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/parser/DefaultJSONParser.java b/src/main/java/com/alibaba/fastjson/parser/DefaultJSONParser.java index d13e8d7dda..559b873eab 100755 --- a/src/main/java/com/alibaba/fastjson/parser/DefaultJSONParser.java +++ b/src/main/java/com/alibaba/fastjson/parser/DefaultJSONParser.java @@ -199,7 +199,8 @@ public final Object parseObject(final Map object, Object fieldName) { 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 (;;) { @@ -264,7 +265,7 @@ public final Object parseObject(final Map object, Object fieldName) { } else { key = lexer.decimalValue(true); } - if (lexer.isEnabled(Feature.NonStringKeyAsString)) { + if (lexer.isEnabled(Feature.NonStringKeyAsString) || isJsonObjectMap) { key = key.toString(); } } catch (NumberFormatException e) { @@ -837,8 +838,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(); + } } } diff --git a/src/main/java/com/alibaba/fastjson/serializer/GuavaCodec.java b/src/main/java/com/alibaba/fastjson/serializer/GuavaCodec.java index c7c23d96ed..4917886ebc 100644 --- a/src/main/java/com/alibaba/fastjson/serializer/GuavaCodec.java +++ b/src/main/java/com/alibaba/fastjson/serializer/GuavaCodec.java @@ -7,6 +7,7 @@ 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; @@ -27,7 +28,12 @@ public void write(JSONSerializer serializer, Object object, Object fieldName, Ty } public T deserialze(DefaultJSONParser parser, Type type, Object fieldName) { - if (type == ArrayListMultimap.class) { + 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()) { diff --git a/src/main/java/com/alibaba/fastjson/serializer/JavaBeanSerializer.java b/src/main/java/com/alibaba/fastjson/serializer/JavaBeanSerializer.java index 8027eba8dc..2196f5808b 100644 --- a/src/main/java/com/alibaba/fastjson/serializer/JavaBeanSerializer.java +++ b/src/main/java/com/alibaba/fastjson/serializer/JavaBeanSerializer.java @@ -320,11 +320,14 @@ protected void write(JSONSerializer serializer, // if (propertyValue == null) { int serialzeFeatures = fieldInfo.serialzeFeatures; + JSONField jsonField = fieldInfo.getAnnotation(); if (beanInfo.jsonType != null) { serialzeFeatures |= SerializerFeature.of(beanInfo.jsonType.serialzeFeatures()); } // beanInfo.jsonType - if (fieldClass == Boolean.class) { + if (jsonField != null && !"".equals(jsonField.defaultValue())) { + propertyValue = jsonField.defaultValue(); + } else if (fieldClass == Boolean.class) { int defaultMask = SerializerFeature.WriteNullBooleanAsFalse.mask; final int mask = defaultMask | SerializerFeature.WriteMapNullValue.mask; if ((!writeAsArray) && (serialzeFeatures & mask) == 0 && (out.features & mask) == 0) { @@ -417,7 +420,9 @@ protected void write(JSONSerializer serializer, // serializer.write(propertyValue); } else { if (!writeAsArray) { - if (writeClassName || !fieldInfo.unwrapped) { + boolean isMap = Map.class.isAssignableFrom(fieldClass); + boolean isJavaBean = !fieldClass.isPrimitive() && !fieldClass.getName().startsWith("java.") || fieldClass == Object.class; + if (writeClassName || !fieldInfo.unwrapped || !(isMap || isJavaBean)) { if (directWritePrefix) { out.write(fieldInfo.name_chars, 0, fieldInfo.name_chars.length); } else { diff --git a/src/main/java/com/alibaba/fastjson/serializer/SerializeConfig.java b/src/main/java/com/alibaba/fastjson/serializer/SerializeConfig.java index 65b328b08a..0370035fe5 100644 --- a/src/main/java/com/alibaba/fastjson/serializer/SerializeConfig.java +++ b/src/main/java/com/alibaba/fastjson/serializer/SerializeConfig.java @@ -236,6 +236,10 @@ public ObjectSerializer createJavaBeanSerializer(SerializeBeanInfo beanInfo) { asm = false; break; } + if (annotation.defaultValue() != null && !"".equals(annotation.defaultValue())) { + asm = false; + break; + } } } diff --git a/src/test/java/com/alibaba/json/bvt/JSONFieldDefaultValueTest.java b/src/test/java/com/alibaba/json/bvt/JSONFieldDefaultValueTest.java new file mode 100644 index 0000000000..a117ac88a5 --- /dev/null +++ b/src/test/java/com/alibaba/json/bvt/JSONFieldDefaultValueTest.java @@ -0,0 +1,257 @@ +package com.alibaba.json.bvt; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.annotation.JSONField; +import junit.framework.TestCase; + +public class JSONFieldDefaultValueTest extends TestCase { + public void test_default_value() throws Exception { + Model m = new Model(); + String s = JSON.toJSONString(m); + System.out.println(s); + Model m2 = JSON.parseObject(s, Model.class); + assertEquals("string", m2.getString()); + assertEquals(false, m2.getaBoolean()); + assertEquals(true, m2.getaBoolean2().booleanValue()); + assertEquals(0, m2.getAnInt()); + assertEquals(888, m2.getInteger().intValue()); + assertEquals(0, m2.getaShort()); + assertEquals(88, m2.getaShort2().shortValue()); + assertEquals('\u0000', m2.getaChar()); + assertEquals('J', m2.getCharacter().charValue()); + assertEquals(0, m2.getaByte()); + assertEquals(8, m2.getaByte2().byteValue()); + assertEquals(0, m2.getaLong()); + assertEquals(8888, m2.getaLong2().longValue()); + assertEquals("0.0", "" + m2.getaFloat()); + assertEquals("8.8", "" + m2.getaFloat2()); + assertEquals("0.0", "" + m2.getaDouble()); + assertEquals("88.88", "" + m2.getaDouble2()); + } + + public void test_not_null() throws Exception { + Model m = new Model("test", true, 888, (short)88, 'J', (byte)8, 8888L, 8.8F, 88.88, false, 999, (short)99, 'C', (byte)9, 9999L, 9.9F, 99.99); + String s = JSON.toJSONString(m); + System.out.println(s); + Model m2 = JSON.parseObject(s, Model.class); + assertEquals("test", m2.getString()); + assertEquals(true, m2.getaBoolean()); + assertEquals(false, m2.getaBoolean2().booleanValue()); + assertEquals(888, m2.getAnInt()); + assertEquals(999, m2.getInteger().intValue()); + assertEquals(88, m2.getaShort()); + assertEquals(99, m2.getaShort2().shortValue()); + assertEquals('J', m2.getaChar()); + assertEquals('C', m2.getCharacter().charValue()); + assertEquals(8, m2.getaByte()); + assertEquals(9, m2.getaByte2().byteValue()); + assertEquals(8888, m2.getaLong()); + assertEquals(9999, m2.getaLong2().longValue()); + assertEquals("8.8", "" + m2.getaFloat()); + assertEquals("9.9", "" + m2.getaFloat2()); + assertEquals("88.88", "" + m2.getaDouble()); + assertEquals("99.99", "" + m2.getaDouble2()); + } + + public static class Model { + @JSONField(defaultValue = "string") + private String string; + + @JSONField(defaultValue = "true") //shouldn't work + private boolean aBoolean; + @JSONField(defaultValue = "888") //shouldn't work + private int anInt; + @JSONField(defaultValue = "88") //shouldn't work + private short aShort; + @JSONField(defaultValue = "J") //shouldn't work + private char aChar; + @JSONField(defaultValue = "8") //shouldn't work + private byte aByte; + @JSONField(defaultValue = "8888") //shouldn't work + private long aLong; + @JSONField(defaultValue = "8.8") //shouldn't work + private float aFloat; + @JSONField(defaultValue = "88.88") //shouldn't work + private double aDouble; + + @JSONField(defaultValue = "true") + private Boolean aBoolean2; + @JSONField(defaultValue = "888") + private Integer integer; + @JSONField(defaultValue = "88") + private Short aShort2; + @JSONField(defaultValue = "J") + private Character character; + @JSONField(defaultValue = "8") + private Byte aByte2; + @JSONField(defaultValue = "8888") + private Long aLong2; + @JSONField(defaultValue = "8.8") + private Float aFloat2; + @JSONField(defaultValue = "88.88") + private Double aDouble2; + + public Model(String string, boolean aBoolean, int anInt, short aShort, char aChar, + byte aByte, long aLong, float aFloat, double aDouble, + Boolean aBoolean2, Integer integer, Short aShort2, Character character, + Byte aByte2, Long aLong2, Float aFloat2, Double aDouble2) { + this.string = string; + this.aBoolean = aBoolean; + this.anInt = anInt; + this.aShort = aShort; + this.aChar = aChar; + this.aByte = aByte; + this.aLong = aLong; + this.aFloat = aFloat; + this.aDouble = aDouble; + this.aBoolean2 = aBoolean2; + this.integer = integer; + this.aShort2 = aShort2; + this.character = character; + this.aByte2 = aByte2; + this.aLong2 = aLong2; + this.aFloat2 = aFloat2; + this.aDouble2 = aDouble2; + } + + public Model() { + } + + public String getString() { + return string; + } + + public void setString(String string) { + this.string = string; + } + + public boolean getaBoolean() { + return aBoolean; + } + + public void setaBoolean(boolean aBoolean) { + this.aBoolean = aBoolean; + } + + public int getAnInt() { + return anInt; + } + + public void setAnInt(int anInt) { + this.anInt = anInt; + } + + public short getaShort() { + return aShort; + } + + public void setaShort(short aShort) { + this.aShort = aShort; + } + + public char getaChar() { + return aChar; + } + + public void setaChar(char aChar) { + this.aChar = aChar; + } + + public byte getaByte() { + return aByte; + } + + public void setaByte(byte aByte) { + this.aByte = aByte; + } + + public long getaLong() { + return aLong; + } + + public void setaLong(long aLong) { + this.aLong = aLong; + } + + public float getaFloat() { + return aFloat; + } + + public void setaFloat(float aFloat) { + this.aFloat = aFloat; + } + + public double getaDouble() { + return aDouble; + } + + public void setaDouble(double aDouble) { + this.aDouble = aDouble; + } + + public Boolean getaBoolean2() { + return aBoolean2; + } + + public void setaBoolean2(Boolean aBoolean2) { + this.aBoolean2 = aBoolean2; + } + + public Integer getInteger() { + return integer; + } + + public void setInteger(Integer integer) { + this.integer = integer; + } + + public Short getaShort2() { + return aShort2; + } + + public void setaShort2(Short aShort2) { + this.aShort2 = aShort2; + } + + public Character getCharacter() { + return character; + } + + public void setCharacter(Character character) { + this.character = character; + } + + public Byte getaByte2() { + return aByte2; + } + + public void setaByte2(Byte aByte2) { + this.aByte2 = aByte2; + } + + public Long getaLong2() { + return aLong2; + } + + public void setaLong2(Long aLong2) { + this.aLong2 = aLong2; + } + + public Float getaFloat2() { + return aFloat2; + } + + public void setaFloat2(Float aFloat2) { + this.aFloat2 = aFloat2; + } + + public Double getaDouble2() { + return aDouble2; + } + + public void setaDouble2(Double aDouble2) { + this.aDouble2 = aDouble2; + } + + } +} diff --git a/src/test/java/com/alibaba/json/bvt/issue_2400/Issue2428.java b/src/test/java/com/alibaba/json/bvt/issue_2400/Issue2428.java new file mode 100644 index 0000000000..eec0663ae4 --- /dev/null +++ b/src/test/java/com/alibaba/json/bvt/issue_2400/Issue2428.java @@ -0,0 +1,34 @@ +package com.alibaba.json.bvt.issue_2400; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.PropertyNamingStrategy; +import com.alibaba.fastjson.serializer.SerializeConfig; +import junit.framework.TestCase; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@NoArgsConstructor +@Data +public class Issue2428 extends TestCase { + private String myName; + private NestedBean nestedBean; + + @AllArgsConstructor + @Data + public static class NestedBean { + private String myId; + } + + public void test_for_issue() { + Issue2428 demoBean = new Issue2428(); + demoBean.setMyName("test name"); + demoBean.setNestedBean(new NestedBean("test id")); + assertEquals("{\"nestedBean\":{\"myId\":\"test id\"},\"myName\":\"test name\"}", JSON.toJSON(demoBean).toString()); + + SerializeConfig serializeConfig = new SerializeConfig(); + serializeConfig.propertyNamingStrategy = PropertyNamingStrategy.SnakeCase; + + assertEquals("{\"my_name\":\"test name\",\"nested_bean\":{\"my_id\":\"test id\"}}", JSON.toJSON(demoBean, serializeConfig).toString()); + } +} diff --git a/src/test/java/com/alibaba/json/bvt/issue_2400/Issue2430.java b/src/test/java/com/alibaba/json/bvt/issue_2400/Issue2430.java new file mode 100644 index 0000000000..6bfcf52c63 --- /dev/null +++ b/src/test/java/com/alibaba/json/bvt/issue_2400/Issue2430.java @@ -0,0 +1,55 @@ +package com.alibaba.json.bvt.issue_2400; + +import com.alibaba.fastjson.JSON; +import com.google.common.collect.ArrayListMultimap; + +import junit.framework.TestCase; + +public class Issue2430 extends TestCase { + public void testForIssue() { + ArrayListMultimap multimap = ArrayListMultimap.create(); + multimap.put("a", "1"); + multimap.put("a", "2"); + multimap.put("a", "3"); + multimap.put("b", "1"); + + VO vo = new VO(); + vo.setMap(multimap); + vo.setName("zhangsan"); + + assertEquals("{\"map\":{\"a\":[\"1\",\"2\",\"3\"],\"b\":[\"1\"]},\"name\":\"zhangsan\"}", + JSON.toJSONString(vo)); + } + + public void testForIssue2() { + String jsonString = "{\"map\":{\"a\":[\"1\",\"2\",\"3\"],\"b\":[\"1\"]},\"name\":\"zhangsan\"}"; + VO vo = JSON.parseObject(jsonString, VO.class); + assertEquals("VO:{name->zhangsan,map->{a=[1, 2, 3], b=[1]}}", vo.toString()); + } + + public static class VO { + private String name; + private ArrayListMultimap map; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public ArrayListMultimap getMap() { + return map; + } + + public void setMap(ArrayListMultimap map) { + this.map = map; + } + + @Override + public String toString() { + return String.format("VO:{name->%s,map->%s}", this.name, this.map.toString()); + } + } +} diff --git a/src/test/java/com/alibaba/json/bvt/issue_2400/Issue2464.java b/src/test/java/com/alibaba/json/bvt/issue_2400/Issue2464.java new file mode 100644 index 0000000000..72328a9fbe --- /dev/null +++ b/src/test/java/com/alibaba/json/bvt/issue_2400/Issue2464.java @@ -0,0 +1,36 @@ +package com.alibaba.json.bvt.issue_2400; + +import com.alibaba.fastjson.JSON; +import junit.framework.TestCase; + +public class Issue2464 extends TestCase { + public void test1() throws Exception { + String json = "[\"Mjg4NDd8MXxjb20uY2Fpbmlhby5pc2ltdS5xLndvcmtmbG93LmNvbGxlY3Quc2NoZWR1bGUuaW1wbC5UYXNrU3RvcENvbGxlY3RDYWxsQmFja0hhbmRsZXJJbXBsfDB8\",1]"; + Object result = JSON.parseArray(json,new Class[]{byte[].class,Integer.class}); + assertEquals(json, JSON.toJSONString(result)); + + result = JSON.parseArray(json,new Class[]{char[].class,Integer.class}); + assertEquals(json, JSON.toJSONString(result)); + } + + public void test2() throws Exception { + String json = "[1,\"Mjg4NDd8MXxjb20uY2Fpbmlhby5pc2ltdS5xLndvcmtmbG93LmNvbGxlY3Quc2NoZWR1bGUuaW1wbC5UYXNrU3RvcENvbGxlY3RDYWxsQmFja0hhbmRsZXJJbXBsfDB8\"]"; + Object result = JSON.parseArray(json,new Class[]{Integer.class,byte[].class}); + assertEquals(json, JSON.toJSONString(result)); + + result = JSON.parseArray(json,new Class[]{Integer.class, char[].class}); + assertEquals(json, JSON.toJSONString(result)); + } + + public void test3() throws Exception { + String json = "[1,\"aaa\",\"bbb\",\"ccc\"]"; + Object result = JSON.parseArray(json, new Class[]{Integer.class, String[].class}); + assertEquals("[1,[\"aaa\",\"bbb\",\"ccc\"]]", JSON.toJSONString(result)); + } + + public void test4() throws Exception { + String json = "[1,97,98,99]"; + Object result = JSON.parseArray(json, new Class[]{Integer.class, byte[].class}); + assertEquals("[1,\"YWJj\"]", JSON.toJSONString(result)); + } +} diff --git a/src/test/java/com/alibaba/json/bvt/issue_2700/Issue2736.java b/src/test/java/com/alibaba/json/bvt/issue_2700/Issue2736.java new file mode 100644 index 0000000000..7965ca23df --- /dev/null +++ b/src/test/java/com/alibaba/json/bvt/issue_2700/Issue2736.java @@ -0,0 +1,13 @@ +package com.alibaba.json.bvt.issue_2700; + +import com.alibaba.fastjson.JSONObject; +import junit.framework.TestCase; + +public class Issue2736 extends TestCase { + public void test_for_issue() throws Exception { + JSONObject s = JSONObject.parseObject("{1:2,3:4}"); + for(String s1 : s.keySet()){ + System.out.println(s1); + } + } +} diff --git a/src/test/java/com/alibaba/json/bvt/serializer/JSONFieldTest_unwrapped_6.java b/src/test/java/com/alibaba/json/bvt/serializer/JSONFieldTest_unwrapped_6.java new file mode 100644 index 0000000000..679bb99c83 --- /dev/null +++ b/src/test/java/com/alibaba/json/bvt/serializer/JSONFieldTest_unwrapped_6.java @@ -0,0 +1,55 @@ +package com.alibaba.json.bvt.serializer; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.annotation.JSONField; +import junit.framework.TestCase; +import org.junit.Assert; + +import java.util.ArrayList; +import java.util.List; + +public class JSONFieldTest_unwrapped_6 extends TestCase { + + public void test_jsonField() throws Exception { + Health vo = new Health(); + List cities = new ArrayList(); + cities.add("Beijing"); + cities.add("Shanghai"); + vo.id = 123; + vo.cities = cities; + + String text = JSON.toJSONString(vo); + Assert.assertEquals("{\"cities\":[\"Beijing\",\"Shanghai\"],\"id\":123}", text); + + Health vo2 = JSON.parseObject(text, Health.class); + assertNotNull(vo2.cities); + assertEquals("Beijing", vo2.cities.get(0)); + assertEquals("Shanghai", vo2.cities.get(1)); + + } + + public void test_null() throws Exception { + Health vo = new Health(); + vo.id = 123; + vo.cities = null; + + String text = JSON.toJSONString(vo); + Assert.assertEquals("{\"id\":123}", text); + } + + public void test_empty() throws Exception { + Health vo = new Health(); + vo.id = 123; + + String text = JSON.toJSONString(vo); + Assert.assertEquals("{\"id\":123}", text); + } + + public static class Health { + @JSONField(unwrapped = true) + public int id; + + @JSONField(unwrapped = true) + public List cities; + } +}