首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >FastJson 反序列化注意事项

FastJson 反序列化注意事项

作者头像
一灰灰blog
发布2018-02-06 14:31:36
6.7K0
发布2018-02-06 14:31:36
举报
文章被收录于专栏:小灰灰小灰灰

问题描述

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

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

解决方案:

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

注意

  • 重复引用,序列化的结果往往不是我们预期的
  • 避免循环引用
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 问题描述
    • 0. Getter/Setter问题
      • 1. 内部类问题
        • 2. 模板类
          • 3. 枚举反序列化
          • 4. 重复引用
          相关产品与服务
          容器服务
          腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档