使用fastJson對json字符串進行反序列化時,有幾個點須要注意一下:java
如咱們但願返回的一個json串爲json
"name" : "name", "isDeleted" : true, "isEmpty" : 1
下面是咱們的定義的dto對象,經過序列化後能獲得咱們預期的結果麼?socket
private String name; private boolean isDeleted; private int isEmpty; public BaseDO() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean isDeleted() { return isDeleted; } public void setDeleted(boolean deleted) { isDeleted = deleted; } public int getIsEmpty() { return isEmpty; } public void setIsEmpty(int isEmpty) { this.isEmpty = isEmpty; }
實際上返回的結果與咱們預期的仍是有差異的ide
解決方案:測試
@JSONField(name = "name") private String name; @JSONField(name = "isDeleted") private boolean isDeleted; @JSONField(name = "isEmpty") private int isEmpty;
注意項:ui
反序列化一個類的內部類時,可能會獲取意想不到的結果,實例以下:this
// 測試用例 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 + '}'; } } }
測試文件容下:lua
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); } } }
輸出結果:debug
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}
從上面的輸出能夠看出,反序列化對象的時候,出現詭異的事情,JsonHello對象的hello元素變成了 nullcode
那麼是如何產生這個問題的呢?
其實也簡單,由於內部類,json反序列化的時候,沒法獲得該類,"hello":{"hello":"innerHello","user":["user1","user2"]}
這個串無法愉快的轉換爲 Hello
對象
這種問題如何避免?
不要反序列化匿名類,內部類!!!
關於模板類,反序列化的主要問題集中在沒法正確的反序列化爲咱們預期的對象,特別是目標對象內部嵌套有容器的時候,這種問題就更明顯了,測試實例以下:
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 + '}'; } }
測試類
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: {}", str); Object obj = JSON.parseObject(str, JsonHello.class); logger.info("Obj: {}", obj); } catch (Exception e) { logger.info("error: {}", e); } } }
聚焦在反序列化的obj對象上,反序列化的結果,debug結果如附圖
咱們但願轉換爲 JsonHello<Hello> 的對象格式,而咱們獲取到的結果呢? 其內部的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); } }
咱們利用FastJson 的 parseObject(str,typeReference) 來實現反序列化的時候,獲得的結果以下,完美!
當序列化的對象中,包含枚舉時,反序列化可能得不到你預期的結果,枚舉對象變成了一個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(); } } }
輸出內容
"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
fastjson序列化的對象中,若存在重複引用的狀況,序列化的結果可能不是咱們預期的結果
@Test public void testJson() { BaseDO baseDO = new BaseDO(); baseDO.setName("base"); baseDO.setDeleted(false); baseDO.setIsEmpty(1); List<Object> res = new ArrayList<>(); res.add("hello"); res.add(baseDO); res.add(123); res.add("no"); res.add(baseDO); res.add(10); res.add(baseDO); String str = JSON.toJSONString(res); logger.info("str :{}", str); }
從運行結果能夠看出,這裏對重複引用,序列化後,給出的是引用標識, 須要避免上面的方法, 能夠顯示關閉循環引用檢測參數
String str2 = JSON.toJSONString(res, SerializerFeature.DisableCircularReferenceDetect);
咱們能夠考慮下,爲何fastJson默認是採起的上面的方式,而不是關閉那個配置參數?
用上面的方法能夠解決重複引用的問題,可是另一種狀況呢 ? 下面的代碼輸出是怎樣的
Map<String,Object> map = new HashMap<>(); map.put("map",map); String str = JSON.toJSONString(map); logger.info("str: {}", str); String str_1 = JSON.toJSONString(map, SerializerFeature.DisableCircularReferenceDetect); logger.info("str_1: {}", str_1);
注意