问题描述
使用fastJson对json字符串进行反序列化时,有几个点需要注意一下:
- 反序列化内部类
- 反序列化模板类
0. Getter/Setter问题
如我们希望返回的一个json串为
1"name" : "name", 2"isDeleted" : true, 3"isEmpty" : 1
下面是我们的定义的dto对象,通过序列化后能得到我们预期的结果么?
1private String name; 2 3private boolean isDeleted; 4 5private int isEmpty; 6 7public BaseDO() { 8} 9 10public String getName() { 11 return name; 12} 13 14public void setName(String name) { 15 this.name = name; 16} 17 18public boolean isDeleted() { 19 return isDeleted; 20} 21 22public void setDeleted(boolean deleted) { 23 isDeleted = deleted; 24} 25 26public int getIsEmpty() { 27 return isEmpty; 28} 29 30public void setIsEmpty(int isEmpty) { 31 this.isEmpty = isEmpty; 32}

实际上返回的结果与我们预期的还是有差别的
解决方案:
1 @JSONField(name = "name") 2private String name; 3 4@JSONField(name = "isDeleted") 5private boolean isDeleted; 6 7@JSONField(name = "isEmpty") 8private int isEmpty;
注意项:
- 默认构造方法
- 使用lombok注解时, 需要注意 isXxx 这种,序列化字符串中的key,可能就变成 xxx
1. 内部类问题
反序列化一个类的内部类时,可能会获取意想不到的结果,实例如下:
1// 测试用例 2package com.mogu.hui.study.json; 3 4import java.util.List; 5 6/** 7 * 用于测试json序列化 8 * Created by yihui on 16/4/22. 9 */ 10public class JsonHello { 11 private String name; 12 13 private Hello hello; 14 15 public JsonHello () { 16 17 } 18 19 public String getName() { 20 return name; 21 } 22 23 public void setName(String name) { 24 this.name = name; 25 } 26 27 public Hello getHello() { 28 return hello; 29 } 30 31 public void setHello(String hello, List<String> user) { 32 Hello h = new Hello(); 33 h.setHello(hello); 34 h.setUser(user); 35 this.hello = h; 36 } 37 38 @Override 39 public String toString() { 40 return "JsonHello{" + 41 "name='" + name + '\'' + 42 ", hello=" + hello + 43 '}'; 44 } 45 46 private class Hello { 47 String hello; 48 List<String> user; 49 50 public Hello(){ 51 52 } 53 54 public String getHello() { 55 return hello; 56 } 57 58 public void setHello(String hello) { 59 this.hello = hello; 60 } 61 62 public List<String> getUser() { 63 return user; 64 } 65 66 public void setUser(List<String> user) { 67 this.user = user; 68 } 69 70 @Override 71 public String toString() { 72 return "Hello{" + 73 "hello='" + hello + '\'' + 74 ", user=" + user + 75 '}'; 76 } 77 } 78 79 80}
测试文件容下:
1package com.mogu.hui.study.json; 2 3import com.alibaba.fastjson.JSON; 4import org.junit.Test; 5import org.slf4j.LoggerFactory; 6import org.slf4j.Logger; 7 8 9import java.util.Arrays; 10 11/** 12 * Created by yihui on 16/4/22. 13 */ 14public class JsonTest { 15 private static Logger logger = LoggerFactory.getLogger(JsonTest.class); 16 17 18 @Test 19 public void innerClassTest() { 20 try { 21 JsonHello jsonHello = new JsonHello(); 22 jsonHello.setName("hello"); 23 jsonHello.setHello("innerHello", Arrays.asList("user1", "user2")); 24 25 String str = JSON.toJSONString(jsonHello); 26 logger.info("Str: {}", str); 27 28 Object obj = JSON.parseObject(str, JsonHello.class); 29 logger.info("Obj: {}", obj); 30 } catch (Exception e) { 31 logger.info("error: {}", e); 32 } 33 } 34}
输出结果:
117:20:08.863 [main] INFO com.mogu.hui.study.json.JsonTest - Str: {"hello":{"hello":"innerHello","user":["user1","user2"]},"name":"hello"} 217:21:44.425 [main] INFO com.mogu.hui.study.json.JsonTest - Obj: JsonHello{name='hello', hello=null}
从上面的输出可以看出,反序列化对象的时候,出现诡异的事情,JsonHello对象的hello元素变成了 null
那么是如何产生这个问题的呢?
其实也简单,因为内部类,json反序列化的时候,无法得到该类,"hello":{"hello":"innerHello","user":["user1","user2"]} 这个串没法愉快的转换为 Hello 对象
这种问题如何避免?
不要反序列化匿名类,内部类!!!
2. 模板类
关于模板类,反序列化的主要问题集中在无法正确的反序列化为我们预期的对象,特别是目标对象内部嵌套有容器的时候,这种问题就更明显了,测试实例如下:
1package com.mogu.hui.study.json; 2 3import java.util.List; 4 5/** 6 * 用于测试json序列化 7 * Created by yihui on 16/4/22. 8 */ 9public class JsonHello<T> { 10 private String name; 11 12 private List<T> list; 13 14 public JsonHello () { 15 16 } 17 18 public String getName() { 19 return name; 20 } 21 22 public void setName(String name) { 23 this.name = name; 24 } 25 26 public List<T> getList() { 27 return list; 28 } 29 30 public void setList(List<T> list) { 31 this.list = list; 32 } 33 34 @Override 35 public String toString() { 36 return "JsonHello{" + 37 "name='" + name + '\'' + 38 ", list=" + list + 39 '}'; 40 } 41} 42 43class Hello { 44 String hello; 45 List<String> user; 46 47 public Hello(){ 48 49 } 50 51 public String getHello() { 52 return hello; 53 } 54 55 public void setHello(String hello) { 56 this.hello = hello; 57 } 58 59 public List<String> getUser() { 60 return user; 61 } 62 63 public void setUser(List<String> user) { 64 this.user = user; 65 } 66 67 @Override 68 public String toString() { 69 return "Hello{" + 70 "hello='" + hello + '\'' + 71 ", user=" + user + 72 '}'; 73 } 74}
测试类
1package com.mogu.hui.study.json; 2 3import com.alibaba.fastjson.JSON; 4import org.junit.Test; 5import org.slf4j.Logger; 6import org.slf4j.LoggerFactory; 7 8import java.util.Arrays; 9 10/** 11 * Created by yihui on 16/4/22. 12 */ 13public class JsonTest { 14 private static Logger logger = LoggerFactory.getLogger(JsonTest.class); 15 16 17 @Test 18 public void innerClassTest() { 19 try { 20 JsonHello<Hello> jsonHello = new JsonHello<>(); 21 jsonHello.setName("hello"); 22 23 Hello hello = new Hello(); 24 hello.setHello("hello1"); 25 hello.setUser(Arrays.asList("user1", "user2")); 26 27 Hello hello2 = new Hello(); 28 hello2.setHello("hello2"); 29 hello2.setUser(Arrays.asList("world1", "world2")); 30 31 jsonHello.setList(Arrays.asList(hello, hello2)); 32 33 34 String str = JSON.toJSONString(jsonHello); 35 logger.info("Str: {}", str); 36 37 Object obj = JSON.parseObject(str, JsonHello.class); 38 logger.info("Obj: {}", obj); 39 } catch (Exception e) { 40 logger.info("error: {}", e); 41 } 42 } 43}
聚焦在反序列化的obj对象上,反序列化的结果,debug结果如附图
我们希望转换为 JsonHello<Hello> 的对象格式,而我们获取到的结果呢? 其内部的list为一个ArrayList对象,list中的元素为 JsonObject

这种问题改如何解决:
利用 TypeReference
1@Test 2public void innerClassTest() { 3 try { 4 JsonHello<Hello> jsonHello = new JsonHello<>(); 5 jsonHello.setName("hello"); 6 7 Hello hello = new Hello(); 8 hello.setHello("hello1"); 9 hello.setUser(Arrays.asList("user1", "user2")); 10 11 Hello hello2 = new Hello(); 12 hello2.setHello("hello2"); 13 hello2.setUser(Arrays.asList("world1", "world2")); 14 15 jsonHello.setList(Arrays.asList(hello, hello2)); 16 17 18 String str = JSON.toJSONString(jsonHello); 19 logger.info("Str: {}", str); 20 21 Object obj = JSON.parseObject(str, JsonHello.class); 22 logger.info("Obj: {}", obj); 23 24 Object obj2 = JSON.parseObject(str, new TypeReference<JsonHello<Hello>>() { 25 }); 26 logger.info("obj2: {}", obj2); 27 28 } catch (Exception e) { 29 logger.info("error: {}", e); 30 } 31}
我们利用FastJson 的 parseObject(str,typeReference) 来实现反序列化的时候,得到的结果如下,完美!

3. 枚举反序列化
当序列化的对象中,包含枚举时,反序列化可能得不到你预期的结果,枚举对象变成了一个String对象, 其实和上面的问题一样,需要
1package com.mogujie.service.rate.base; 2 3import com.alibaba.fastjson.JSON; 4import com.alibaba.fastjson.TypeReference; 5import org.junit.Test; 6 7import java.util.ArrayList; 8import java.util.List; 9 10/** 11 * Created by yihui on 16/10/31. 12 */ 13public class JsonTest { 14 15 public enum MyDay { 16 YESDAY("SUNDAY"), TODAY("MONDAY"), TOMORROW("TUESDAY"); 17 18 private String today; 19 20 MyDay(String today) { 21 this.today = today; 22 } 23 24 public String getToday() { 25 return today; 26 } 27 28 @Override 29 public String toString() { 30 return "MyDay{" + 31 "today='" + today + '\'' + 32 '}'; 33 } 34 } 35 36 class TTT { 37 MyDay myDay; 38 39 public TTT() { 40 } 41 42 public TTT(MyDay myDay) { 43 this.myDay = myDay; 44 } 45 46 @Override 47 public String toString() { 48 return "TTT{" + 49 "myDay=" + myDay + 50 '}'; 51 } 52 } 53 54 55 private MyDay getDay() { 56 return MyDay.TODAY; 57 } 58 59 60 @Test 61 public void testJson() { 62 String str = JSON.toJSONString(getDay()); 63 System.out.println(str); 64 65 // 这样反序列化ok 66 MyDay myDay = JSON.parseObject(str, MyDay.class); 67 System.out.println(myDay); 68 69 70 List<MyDay> myDayList = new ArrayList<>(); 71 myDayList.add(MyDay.TODAY); 72 myDayList.add(MyDay.TOMORROW); 73 String str2 = JSON.toJSONString(myDayList); 74 System.out.println(str2); 75 76 // 反序列化失败, 和模板类的问题一样 77 try { 78 List<MyDay> out = JSON.parseObject(str2, List.class); 79 for (MyDay myDay1 : out) { 80 System.out.println(myDay1); 81 } 82 }catch (Exception e) { 83 e.printStackTrace(); 84 } 85 86 87 // 采用这种方式,反序列化ok 88 TypeReference<List<MyDay>> typeReference = new TypeReference<List<MyDay>>(){ 89 }; 90 try { 91 List<MyDay> out = JSON.parseObject(str2, typeReference); 92 for (MyDay myDay1 : out) { 93 System.out.println(myDay1); 94 } 95 }catch (Exception e) { 96 e.printStackTrace(); 97 } 98 99 100 System.out.println("------------"); 101 102 TTT tt = new TTT(MyDay.TODAY); 103 String str3 = JSON.toJSONString(tt); 104 System.out.println(str3); 105 106 // 直接反序列化异常 107 try { 108 TTT recover = JSON.parseObject(str3, TTT.class); 109 System.out.println(recover); 110 } catch (Exception e) { 111 e.printStackTrace(); 112 } 113 } 114 115}
输出内容
1"TODAY" 2MyDay{today='MONDAY'} 3["TODAY","TOMORROW"] 4java.lang.ClassCastException: java.lang.String cannot be cast to com.mogujie.service.rate.base.JsonTest$MyDay 5 at com.mogujie.service.rate.base.JsonTest.testJson(JsonTest.java:79) 6 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 7 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) 8 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 9 at java.lang.reflect.Method.invoke(Method.java:606) 10 at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47) 11 at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) 12 at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44) 13 at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) 14 at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271) 15 at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70) 16 at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50) 17 at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238) 18 at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63) 19 at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236) 20 at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53) 21 at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229) 22 at org.junit.runners.ParentRunner.run(ParentRunner.java:309) 23 at org.junit.runner.JUnitCore.run(JUnitCore.java:160) 24 at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69) 25 at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234) 26 at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74) 27MyDay{today='MONDAY'} 28MyDay{today='TUESDAY'} 29------------ 30{} 31com.alibaba.fastjson.JSONException: create instance error, class com.mogujie.service.rate.base.JsonTest$TTT 32 at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.createInstance(JavaBeanDeserializer.java:116) 33 at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:356) 34 at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:135) 35 at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:551) 36 at com.alibaba.fastjson.JSON.parseObject(JSON.java:251) 37 at com.alibaba.fastjson.JSON.parseObject(JSON.java:227) 38 at com.alibaba.fastjson.JSON.parseObject(JSON.java:186) 39 at com.alibaba.fastjson.JSON.parseObject(JSON.java:304) 40 at com.mogujie.service.rate.base.JsonTest.testJson(JsonTest.java:108) 41 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 42 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) 43 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 44 at java.lang.reflect.Method.invoke(Method.java:606) 45 at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47) 46 at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) 47 at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44) 48 at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) 49 at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271) 50 at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70) 51 at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50) 52 at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238) 53 at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63) 54 at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236) 55 at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53) 56 at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229) 57 at org.junit.runners.ParentRunner.run(ParentRunner.java:309) 58 at org.junit.runner.JUnitCore.run(JUnitCore.java:160) 59 at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69) 60 at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234) 61 at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74) 62Caused by: java.lang.NullPointerException 63 at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.createInstance(JavaBeanDeserializer.java:113) 64 ... 29 more 65Disconnected from the target VM, address: '127.0.0.1:52140', transport: 'socket' 66 67Process finished with exit code 0
4. 重复引用
fastjson序列化的对象中,若存在重复引用的情况,序列化的结果可能不是我们预期的结果
1@Test 2public void testJson() { 3 BaseDO baseDO = new BaseDO(); 4 baseDO.setName("base"); 5 baseDO.setDeleted(false); 6 baseDO.setIsEmpty(1); 7 8 9 List<Object> res = new ArrayList<>(); 10 res.add("hello"); 11 res.add(baseDO); 12 res.add(123); 13 res.add("no"); 14 res.add(baseDO); 15 res.add(10); 16 res.add(baseDO); 17 18 String str = JSON.toJSONString(res); 19 logger.info("str :{}", str); 20}

从运行结果可以看出,这里对重复引用,序列化后,给出的是引用标识, 需要避免上面的方法, 可以显示关闭循环引用检测参数
String str2 = JSON.toJSONString(res, SerializerFeature.DisableCircularReferenceDetect);
我们可以考虑下,为什么fastJson默认是采取的上面的方式,而不是关闭那个配置参数?
用上面的方法可以解决重复引用的问题,但是另外一种情况呢 ? 下面的代码输出是怎样的
1Map<String,Object> map = new HashMap<>(); 2map.put("map",map); 3 4String str = JSON.toJSONString(map); 5logger.info("str: {}", str); 6 7String str_1 = JSON.toJSONString(map, SerializerFeature.DisableCircularReferenceDetect); 8logger.info("str_1: {}", str_1);
注意
- 重复引用,序列化的结果往往不是我们预期的
- 避免循环引用