百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

聊聊fastjson反序列化的那些坑

suiw9 2024-11-17 15:49 49 浏览 0 评论

关于json反序列化的几个问题。

场景

json作为一种可读性很强的格式,现在是广泛使用的。这里罗列两个我需要对json字符串进行反序列化的场景:

  1. 日志/数据库存储了接口的入参数据,希望通过反序列化来反查问题。
  2. 远程调用传过来的数据为json串,需要反序列化后使用。

而集团内使用得最多的就是FastJson了,这里分享几个我遇到的关于json反序列化的问题。

问题

? getter与setter类型不同

fastjson的序列化/反序列化是通过getter/setter方法来的,以如下User类为例,money字段的getter/setter类型不同:

public class User{

    public User(Long money, Date birthday){

      this.money = money;

        this.birthday = birthday;

    }

    

   private Long money;

    

    private Date birthday;

   

    public Money getMoney(){

      Money money = new Money();

        money.setMoney(this.money);

        money.setLocation("China");

        return money;

    }

    

    public void setMoney(long money){

      this.money = money;

    }

    

    public Date getBirthday(){

      return birthday;

    }

    

    public void setBirthDay(Date birthday){

      this.birthday = birthday;

    }

    

}




@Data

public class Money{

    

    private Long money;

    

    private String Location;

}

当我们用fastjson序列化User类的对象后,会得到如下字符串:

{

    "birthday":1639129158598,

    "money":{

        "location":"China",

        "money":10

    }

}

而反序列化,则会报错:

原因是setter需要的类型是Long,但无法用Long的解析器来解析Money格式的数据。

? 没有无参构造函数

为了解决1中的问题,我将User对象反序列化为JSONObject,然后修改对应的字段为Long类型,然后进行反序列化。会发现,依然报错:

? Gson与FastJson对Date类的序列化/反序列化方式不同

为了解决2中的问题,决定将money字段改为Long类型后,转成字符串给Gson进行反序列化。原因是Gson的所有反序列化行为,都是基于反射做的,实际这也能避免fastjson存在的许多问题。然后发现,很不幸的,还是报错:

原因是Gson与FastJson对Date类的序列化/反序列化方式不同,fastjson会将Date序列化为时间戳。

? 系统中存在接口类

阅读fastjson代码com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer#createInstance可以看到,对于接口类,它是新建的一个代理来做反序列化的:

这会存在一个问题,就是使用像hessian这种序列化/反序列化的远程调用时,这个代理对象在远端并无法被成功反序列化,从而导致远程调用异常。同时,一些基于人类类型做判断的逻辑也无法进行了。

解法

? getter与setter类型不同

可以将json字符串解析为JSONObject,然后再操作修改对应位置的值为其真实值,最后调用JSON.toJavaObject反序列化为对应类。

? 没有无参构造函数

可以用Gson进行反序列化。Gson的所有反序列化行为,都是基于反射做的,实际这也能避免fastjson存在的许多问题。

? Gson与FastJson对Date类的序列化/反序列化方式不同

Gson可以添加对某种类的解析方法,这里使用如下方式添加Date类的定制解析方法,并创建Gson对象:

Gson gson = new GsonBuilder().registerTypeAdapter(

    Date.class, new TypeAdapter<Date>(){




        @Override

        public void write(JsonWriter out, Date value) throws IOException {

            if (value == null){

                out.nullValue();




            }else{

                out.value(value.getTime());

            }

        }




        @Override

        public Date read(JsonReader in) throws IOException {

            if (in != null){

                return new Date(in.nextLong());

            }else{

                return null;

            }

        }

    }).create();

? 系统中存在接口类

fastjson可以添加类的自定义反序列化方法:实现ObjectDeserializer接口,然后调用ParserConfig.getGlobalInstance().putDeserializer(Type, ObjectDeserializer)即可。

但是我们来看ObjectDeserializer接口需要实现的方法:

会发现,理解它并用它来反序列化自定义复杂类型,学习成本是比较高的。

通过阅读代码,可以看到,fastjson中对象的反序列化一般都是使用的com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer,它是继承ObjectDeserializer的,并且已经实现了几乎所有能力,所以实际上,我们继承它,然后重写其中的某些方法即可。这里我给出如下所示的重写方法并放进ParserConfig.getGlobalInstance()中:

ParserConfig.getGlobalInstance().putDeserializer(clazz, 

    new JavaBeanDeserializer(ParserConfig.getGlobalInstance(), clazz) {




    @Override

    public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName){

        //解析为JSONObject,方便业务使用并反序列化为对应的子类

        JSONObject jsonObj = this.deserialze(parser, JSONObject.class, fieldName, 0);




        //业务根据JSONObject创建对应子类对象逻辑

        T obj = ...




        return obj;

    }




    @Override

    public Object createInstance(Map<String, Object> map, ParserConfig config) //

        throws IllegalArgumentException {

        JSONObject jsonObj = null;

        if (map instanceof JSONObject){

            jsonObj = (JSONObject)map;

        }else if(map != null){

          jsonObj = new JSONObject(map);

        }




        //业务根据JSONObject创建对应子类对象逻辑

        T obj = ...




        return obj;

    }




});

这里重写了两个方法,第一个deserialze是ObjectDeserializer的接口方法,这里利用了JavaBeanDeserializer现有能力,将它转成JSONObject来给业务使用,学习成本会大大降低。

第二个方法是createInstance(Map<String, Object> map, ParserConfig config),它的场景是,如果要反序列化的类不是接口类,而是有子类的父类(同样需要根据成员来判断应该反序列化为哪种具体的子类),并且此时是用JSON.toJavaObject反序列化JSONObject,那么如下图所示,它首先会调用createInstance方法(在com.alibaba.fastjson.util.TypeUtils#castToJavaBean(java.util.Map<java.lang.String,java.lang.Object>, java.lang.Class<T>, com.alibaba.fastjson.parser.ParserConfig)中),因此我们也重写该方法。

写在最后

对于反序列化中的这些问题,大部分都能通过修改类成员模型及方法来解决,而其他域的数据,无法修改它的类模型,则只能用无侵入的方法来解决反序列化问题,对于一些复杂类,反序列化是困难重重的,但,办法总比问题多。

相关推荐

俄罗斯的 HTTPS 也要被废了?(俄罗斯网站关闭)

发布该推文的ScottHelme是一名黑客,SecurityHeaders和ReportUri的创始人、Pluralsight作者、BBC常驻黑客。他表示,CAs现在似乎正在停止为俄罗斯域名颁发...

如何强制所有流量使用 HTTPS一网上用户

如何强制所有流量使用HTTPS一网上用户使用.htaccess强制流量到https的最常见方法可能是使用.htaccess重定向请求。.htaccess是一个简单的文本文件,简称为“.h...

https和http的区别(https和http有何区别)

“HTTPS和HTTP都是数据传输的应用层协议,区别在于HTTPS比HTTP安全”。区别在哪里,我们接着往下看:...

快码住!带你十分钟搞懂HTTP与HTTPS协议及请求的区别

什么是协议?网络协议是计算机之间为了实现网络通信从而达成的一种“约定”或“规则”,正是因为这个“规则”的存在,不同厂商的生产设备、及不同操作系统组成的计算机之间,才可以实现通信。简单来说,计算机与网络...

简述HTTPS工作原理(简述https原理,以及与http的区别)

https是在http协议的基础上加了一层SSL(由网景公司开发),加密由ssl实现,它的目的是为用户提供对网站服务器的身份认证(需要CA),以至于保护交换数据的隐私和完整性,原理如图示。1、客户端发...

21、HTTPS 有几次握手和挥手?HTTPS 的原理什么是(高薪 常问)

HTTPS是3次握手和4次挥手,和HTTP是一样的。HTTPS的原理...

一次安全可靠的通信——HTTPS原理

为什么HTTPS协议就比HTTP安全呢?一次安全可靠的通信应该包含什么东西呢,这篇文章我会尝试讲清楚这些细节。Alice与Bob的通信...

为什么有的网站没有使用https(为什么有的网站点不开)

有的网站没有使用HTTPS的原因可能涉及多个方面,以下是.com、.top域名的一些见解:服务器性能限制:HTTPS使用公钥加密和私钥解密技术,这要求服务器具备足够的计算能力来处理加解密操作。如果服务...

HTTPS是什么?加密原理和证书。SSL/TLS握手过程

秘钥的产生过程非对称加密...

图解HTTPS「转」(图解http 完整版 彩色版 pdf)

我们都知道HTTPS能够加密信息,以免敏感信息被第三方获取。所以很多银行网站或电子邮箱等等安全级别较高的服务都会采用HTTPS协议。...

HTTP 和 HTTPS 有何不同?一文带你全面了解

随着互联网时代的高速发展,Web服务器和客户端之间的安全通信需求也越来越高。HTTP和HTTPS是两种广泛使用的Web通信协议。本文将介绍HTTP和HTTPS的区别,并探讨为什么HTTPS已成为We...

HTTP与HTTPS的区别,详细介绍(http与https有什么区别)

HTTP与HTTPS介绍超文本传输协议HTTP协议被用于在Web浏览器和网站服务器之间传递信息,HTTP协议以明文方式发送内容,不提供任何方式的数据加密,如果攻击者截取了Web浏览器和网站服务器之间的...

一文让你轻松掌握 HTTPS(https详解)

一文让你轻松掌握HTTPS原文作者:UC国际研发泽原写在最前:欢迎你来到“UC国际技术”公众号,我们将为大家提供与客户端、服务端、算法、测试、数据、前端等相关的高质量技术文章,不限于原创与翻译。...

如何在Spring Boot应用程序上启用HTTPS?

HTTPS是HTTP的安全版本,旨在提供传输层安全性(TLS)[安全套接字层(SSL)的后继产品],这是地址栏中的挂锁图标,用于在Web服务器和浏览器之间建立加密连接。HTTPS加密每个数据包以安全方...

一文彻底搞明白Http以及Https(http0)

早期以信息发布为主的Web1.0时代,HTTP已可以满足绝大部分需要。证书费用、服务器的计算资源都比较昂贵,作为HTTP安全扩展的HTTPS,通常只应用在登录、交易等少数环境中。但随着越来越多的重要...

取消回复欢迎 发表评论: