FastJson 反序列化注意事项

Stella981
• 阅读 854

问题描述

使用fastJson对json字符串进行反序列化时,有几个点需要注意一下:

  • 反序列化内部类
  • 反序列化模板类

0. Getter/Setter问题

如我们希望返回的一个json串为

"name" : "name",
"isDeleted" : true,
"isEmpty" : 1

下面是我们的定义的dto对象,通过序列化后能得到我们预期的结果么?

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

FastJson 反序列化注意事项

实际上返回的结果与我们预期的还是有差别的

解决方案:

 @JSONField(name = "name")
private String name;

@JSONField(name = "isDeleted")
private boolean isDeleted;

@JSONField(name = "isEmpty")
private int isEmpty;

注意项:

  • 默认构造方法
  • 使用lombok注解时, 需要注意 isXxx 这种,序列化字符串中的key,可能就变成 xxx

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


}

测试文件容下:

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

输出结果:

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元素变成了 null

那么是如何产生这个问题的呢?

其实也简单,因为内部类,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 的对象格式,而我们获取到的结果呢? 其内部的list为一个ArrayList对象,list中的元素为 JsonObject

FastJson 反序列化注意事项

这种问题改如何解决:

利用 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) 来实现反序列化的时候,得到的结果如下,完美!

FastJson 反序列化注意事项

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

FastJson 反序列化注意事项

从运行结果可以看出,这里对重复引用,序列化后,给出的是引用标识, 需要避免上面的方法, 可以显示关闭循环引用检测参数

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

注意

  • 重复引用,序列化的结果往往不是我们预期的
  • 避免循环引用
点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
2年前
java将前端的json数组字符串转换为列表
记录下在前端通过ajax提交了一个json数组的字符串,在后端如何转换为列表。前端数据转化与请求varcontracts{id:'1',name:'yanggb合同1'},{id:'2',name:'yanggb合同2'},{id:'3',name:'yang
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
2年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
ES6 新增的数组的方法
给定一个数组letlist\//wu:武力zhi:智力{id:1,name:'张飞',wu:97,zhi:10},{id:2,name:'诸葛亮',wu:55,zhi:99},{id:3,name:'赵云',wu:97,zhi:66},{id:4,na
Wesley13 Wesley13
2年前
unity将 -u4E00 这种 编码 转汉字 方法
 unity中直接使用 JsonMapper.ToJson(对象),取到的字符串,里面汉字可能是\\u4E00类似这种其实也不用转,服务器会通过类似fastjson发序列化的方式,将json转对象,获取对象的值就是中文但是有时服务器要求将传参中字符串中类似\\u4E00这种转汉字,就需要下面 publ
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
2个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这