网站首页 > java教程 正文
关于json反序列化的几个问题。
场景
json作为一种可读性很强的格式,现在是广泛使用的。这里罗列两个我需要对json字符串进行反序列化的场景:
- 日志/数据库存储了接口的入参数据,希望通过反序列化来反查问题。
- 远程调用传过来的数据为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)中),因此我们也重写该方法。
写在最后
对于反序列化中的这些问题,大部分都能通过修改类成员模型及方法来解决,而其他域的数据,无法修改它的类模型,则只能用无侵入的方法来解决反序列化问题,对于一些复杂类,反序列化是困难重重的,但,办法总比问题多。
猜你喜欢
- 2024-11-04 快速处理Kafka反序列化错误(kafka自定义反序列化)
- 2024-11-04 又一个反序列化漏洞,我服了...(反序列化漏洞修复方案)
- 2024-11-04 Java代码示例:如何使用 serialVersionUID处理序列化
- 2024-11-04 Java 序列化机制(java序列化过程)
- 2024-11-04 SpringBoot整合Grpc实现跨语言RPC通讯
- 2024-11-04 php和java及python3.10的序列化和反序列化
- 2024-11-04 Java修炼终极指南:133 避免在反序列化时发生DoS攻击
- 2024-11-04 Java序列化 3 连问,这太难了吧(在线序列化工具)
- 2024-11-04 避免使用Java序列化(serializable 防止序列化)
- 2024-11-04 试验java反序列化炸弹碰到的一个HashSet问题
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)