问题描述
使用fastJson对json字符串进行反序列化时,有几个点需要注意一下:
- 反序列化内部类
- 反序列化模板类
1. 内部类问题
反序列化一个类的内部类时,可能会获取意想不到的结果,实例如下:
// 测试用例 package com.mogu.hui.study.json; import java.util.List; /** * 用于测试json序列化 * Created by yihui on 16/4/22. */ public class JsonHello { private String name; private Hello hello; public JsonHello () { } public String getName() { return name; } public void setName(String name) { this.name = name; } public Hello getHello() { return hello; } public void setHello(String hello,List<String> user) { Hello h = new Hello(); h.setHello(hello); h.setUser(user); this.hello = h; } @Override public String toString() { return "JsonHello{" + "name='" + name + '\'' + ",hello=" + hello + '}'; } private class Hello { String hello; List<String> user; public Hello(){ } public String getHello() { return hello; } public void setHello(String hello) { this.hello = hello; } public List<String> getUser() { return user; } public void setUser(List<String> user) { this.user = user; } @Override public String toString() { return "Hello{" + "hello='" + hello + '\'' + ",user=" + user + '}'; } } }@H_403_15@测试文件容下:
package com.mogu.hui.study.json; import com.alibaba.fastjson.JSON; import org.junit.Test; import org.slf4j.LoggerFactory; import org.slf4j.Logger; import java.util.Arrays; /** * Created by yihui on 16/4/22. */ public class JsonTest { private static Logger logger = LoggerFactory.getLogger(JsonTest.class); @Test public void innerClassTest() { try { JsonHello jsonHello = new JsonHello(); jsonHello.setName("hello"); jsonHello.setHello("innerHello",Arrays.asList("user1","user2")); String str = JSON.toJSONString(jsonHello); logger.info("Str: {}",str); Object obj = JSON.parSEObject(str,JsonHello.class); logger.info("Obj: {}",obj); } catch (Exception e) { logger.info("error: {}",e); } } }@H_403_15@输出结果:
17:20:08.863 [main] INFO com.mogu.hui.study.json.JsonTest - Str: {"hello":{"hello":"innerHello","user":["user1","user2"]},"name":"hello"} 17:21:44.425 [main] INFO com.mogu.hui.study.json.JsonTest - Obj: JsonHello{name='hello',hello=null}@H_403_15@从上面的输出可以看出,反序列化对象的时候,出现诡异的事情,JsonHello对象的hello元素变成了 null
那么是如何产生这个问题的呢?
其实也简单,因为内部类,json反序列化的时候,无法得到该类,
"hello":{"hello":"innerHello","user2"]}
这个串没法愉快的转换为Hello
对象这种问题如何避免?
不要反序列化匿名类,内部类!!!
2. 模板类
关于模板类,反序列化的主要问题集中在无法正确的反序列化为我们预期的对象,特别是目标对象内部嵌套有容器的时候,这种问题就更明显了,测试实例如下:
package com.mogu.hui.study.json; import java.util.List; /** * 用于测试json序列化 * Created by yihui on 16/4/22. */ public class JsonHello<T> { private String name; private List<T> list; public JsonHello () { } public String getName() { return name; } public void setName(String name) { this.name = name; } public List<T> getList() { return list; } public void setList(List<T> list) { this.list = list; } @Override public String toString() { return "JsonHello{" + "name='" + name + '\'' + ",list=" + list + '}'; } } class Hello { String hello; List<String> user; public Hello(){ } public String getHello() { return hello; } public void setHello(String hello) { this.hello = hello; } public List<String> getUser() { return user; } public void setUser(List<String> user) { this.user = user; } @Override public String toString() { return "Hello{" + "hello='" + hello + '\'' + ",user=" + user + '}'; } }@H_403_15@测试类
package com.mogu.hui.study.json; import com.alibaba.fastjson.JSON; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Arrays; /** * Created by yihui on 16/4/22. */ public class JsonTest { private static Logger logger = LoggerFactory.getLogger(JsonTest.class); @Test public void innerClassTest() { try { JsonHello<Hello> jsonHello = new JsonHello<>(); jsonHello.setName("hello"); Hello hello = new Hello(); hello.setHello("hello1"); hello.setUser(Arrays.asList("user1","user2")); Hello hello2 = new Hello(); hello2.setHello("hello2"); hello2.setUser(Arrays.asList("world1","world2")); jsonHello.setList(Arrays.asList(hello,hello2)); String str = JSON.toJSONString(jsonHello); logger.info("Str: {}",e); } } }@H_403_15@聚焦在反序列化的obj对象上,反序列化的结果,debug结果如附图
我们希望转换为 JsonHello
的对象格式,而我们获取到的结果呢? 其内部的list为一个ArrayList对象,list中的元素为 JsonObject 这种问题改如何解决:
利用 TypeReference
@Test public void innerClassTest() { try { JsonHello<Hello> jsonHello = new JsonHello<>(); jsonHello.setName("hello"); Hello hello = new Hello(); hello.setHello("hello1"); hello.setUser(Arrays.asList("user1","user2")); Hello hello2 = new Hello(); hello2.setHello("hello2"); hello2.setUser(Arrays.asList("world1","world2")); jsonHello.setList(Arrays.asList(hello,hello2)); String str = JSON.toJSONString(jsonHello); logger.info("Str: {}",str); Object obj = JSON.parSEObject(str,JsonHello.class); logger.info("Obj: {}",obj); Object obj2 = JSON.parSEObject(str,new TypeReference<JsonHello<Hello>>() { }); logger.info("obj2: {}",obj2); } catch (Exception e) { logger.info("error: {}",e); } }@H_403_15@我们利用FastJson 的 parSEObject(str,typeReference) 来实现反序列化的时候,得到的结果如下,完美!
3. 枚举反序列化
当序列化的对象中,包含枚举时,反序列化可能得不到你预期的结果,枚举对象变成了一个String对象, 其实和上面的问题一样,需要
package com.mogujie.service.rate.base; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.TypeReference; import org.junit.Test; import java.util.ArrayList; import java.util.List; /** * Created by yihui on 16/10/31. */ public class JsonTest { public enum MyDay { YESDAY("SUNDAY"),TODAY("MONDAY"),TOMORROW("TUESDAY"); private String today; MyDay(String today) { this.today = today; } public String getToday() { return today; } @Override public String toString() { return "MyDay{" + "today='" + today + '\'' + '}'; } } class TTT { MyDay myDay; public TTT() { } public TTT(MyDay myDay) { this.myDay = myDay; } @Override public String toString() { return "TTT{" + "myDay=" + myDay + '}'; } } private MyDay getDay() { return MyDay.TODAY; } @Test public void testJson() { String str = JSON.toJSONString(getDay()); System.out.println(str); // 这样反序列化ok MyDay myDay = JSON.parSEObject(str,MyDay.class); System.out.println(myDay); List<MyDay> myDayList = new ArrayList<>(); myDayList.add(MyDay.TODAY); myDayList.add(MyDay.TOMORROW); String str2 = JSON.toJSONString(myDayList); System.out.println(str2); // 反序列化失败,和模板类的问题一样 try { List<MyDay> out = JSON.parSEObject(str2,List.class); for (MyDay myDay1 : out) { System.out.println(myDay1); } }catch (Exception e) { e.printStackTrace(); } // 采用这种方式,反序列化ok TypeReference<List<MyDay>> typeReference = new TypeReference<List<MyDay>>(){ }; try { List<MyDay> out = JSON.parSEObject(str2,typeReference); for (MyDay myDay1 : out) { System.out.println(myDay1); } }catch (Exception e) { e.printStackTrace(); } System.out.println("------------"); TTT tt = new TTT(MyDay.TODAY); String str3 = JSON.toJSONString(tt); System.out.println(str3); // 直接反序列化异常 try { TTT recover = JSON.parSEObject(str3,TTT.class); System.out.println(recover); } catch (Exception e) { e.printStackTrace(); } } }@H_403_15@"TODAY" MyDay{today='MONDAY'} ["TODAY","TOMORROW"] java.lang.ClassCastException: java.lang.String cannot be cast to com.mogujie.service.rate.base.JsonTest$MyDay at com.mogujie.service.rate.base.JsonTest.testJson(JsonTest.java:79) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229) at org.junit.runners.ParentRunner.run(ParentRunner.java:309) at org.junit.runner.JUnitCore.run(JUnitCore.java:160) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74) MyDay{today='MONDAY'} MyDay{today='TUESDAY'} ------------ {} com.alibaba.fastjson.JSONException: create instance error,class com.mogujie.service.rate.base.JsonTest$TTT at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.createInstance(JavaBeanDeserializer.java:116) at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:356) at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:135) at com.alibaba.fastjson.parser.DefaultJSONParser.parSEObject(DefaultJSONParser.java:551) at com.alibaba.fastjson.JSON.parSEObject(JSON.java:251) at com.alibaba.fastjson.JSON.parSEObject(JSON.java:227) at com.alibaba.fastjson.JSON.parSEObject(JSON.java:186) at com.alibaba.fastjson.JSON.parSEObject(JSON.java:304) at com.mogujie.service.rate.base.JsonTest.testJson(JsonTest.java:108) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229) at org.junit.runners.ParentRunner.run(ParentRunner.java:309) at org.junit.runner.JUnitCore.run(JUnitCore.java:160) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74) Caused by: java.lang.NullPointerException at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.createInstance(JavaBeanDeserializer.java:113) ... 29 more Disconnected from the target VM,address: '127.0.0.1:52140',transport: 'socket' Process finished with exit code 0@H_403_15@