FastJson 反序列化注意事項

問題描述

使用fastJson對json字符串進行反序列化時,有幾個點須要注意一下:java

  • 反序列化內部類
  • 反序列化模板類

0. Getter/Setter問題

如咱們但願返回的一個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

  • 默認構造方法
  • 使用lombok註解時, 須要注意 isXxx 這種,序列化字符串中的key,可能就變成 xxx

1. 內部類問題

反序列化一個類的內部類時,可能會獲取意想不到的結果,實例以下: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 對象

這種問題如何避免?

不要反序列化匿名類,內部類!!!

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 +
                '}';
    }
}

測試類

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) 來實現反序列化的時候,獲得的結果以下,完美!

輸入圖片說明

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();
        }
    }

}

輸出內容

"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

4. 重複引用

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);

注意

  • 重複引用,序列化的結果每每不是咱們預期的
  • 避免循環引用
相關文章
相關標籤/搜索