前言
最近做项目,发现fastjson在parseObject的过程中,出现部分属性丢失,然而json字符串确是存在属性值的,然后试了gson,发现正常,但是jackson某些情况下也存在问题。下面分析
1. demo
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.10.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.10.0</version>
</dependency>
</dependencies>
bean
package com.feng.fastjson.bean;
import lombok.ToString;
@ToString
public class Person {
private String sex;
private String zoo;
private String beard;
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getBeard() {
return beard;
}
public void setBeard(String beard) {
if ("Man".equals(sex)) {
if (this.beard == null) {
this.beard = "man has";
} else {
this.beard = beard;
}
}
}
public String getZoo() {
return zoo;
}
public void setZoo(String zoo) {
if ("Man".equals(sex)) {
if (this.zoo == null) {
this.zoo = "zoo";
} else {
this.zoo = zoo;
}
}
}
}
bean是特殊加工的,不是原生set方法,是根据业务需求改造后,属性之间有依赖和相关性。
main方法
package com.feng.fastjson;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.feng.fastjson.bean.Person;
import com.google.gson.Gson;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
Person person = new Person();
person.setSex("Man");
person.setBeard("has");
person.setZoo("zoo");
String jsonStr = JSONObject.toJSONString(person);
System.out.println("json\t"+jsonStr);
System.out.println();
String str = "{\"sex\":\"Man\",\"beard\":\"man has\",\"zoo\":\"zoo\"}";
Person person1 = JSON.parseObject(jsonStr, Person.class);
System.out.println("fastjson:\t"+person1);
Person person11 = JSON.parseObject(str, Person.class);
System.out.println("fastjson 11:\t"+person11);
System.out.println();
Person person2 = new Gson().fromJson(jsonStr, Person.class);
System.out.println("gson:\t"+person2);
System.out.println();
ObjectMapper mapper = new ObjectMapper();
Person person3 = mapper.readValue(jsonStr.getBytes(), Person.class);
System.out.println("jackson:\t"+person3);
Person person33 = mapper.readValue(str.getBytes(), Person.class);
System.out.println("jackson 33:\t"+person33);
}
}
2. 结果
运行上面的main
json {"beard":"man has","sex":"Man","zoo":"zoo"}
fastjson: Person(sex=Man, zoo=zoo, beard=null)
fastjson 11: Person(sex=Man, zoo=zoo, beard=man has)
gson: Person(sex=Man, zoo=zoo, beard=man has)
jackson: Person(sex=Man, zoo=zoo, beard=null)
jackson 33: Person(sex=Man, zoo=zoo, beard=man has)
可以看出,除了gson外,fastjson与jackson在反序列化的过程中,某些情况丢失了属性值
根据上面的测试,可以初步推断,set方法在fastjson与jackson中是按照json字符串的顺序转化生成bean的。在set时,由于json字符串的属性的字段先后顺序,造成部分属性设置依赖的其他属性未设置,从而丢失属性字段。
3. fastjson源码分析
直接跟踪源码,发现

继续,创建反序列化器
然后反序列化json字符串,使用ASM字节码生成技术,动态生产反序列化器对象

核心关注反序列化方法,将code生成class文件,反编译
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.alibaba.fastjson.parser.deserializer;
import com.alibaba.fastjson.parser.DefaultJSONParser;
import com.alibaba.fastjson.parser.JSONLexerBase;
import com.alibaba.fastjson.parser.ParseContext;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.util.JavaBeanInfo;
import com.feng.fastjson.bean.Person;
import java.lang.reflect.Type;
public class FastjsonASMDeserializer_1_Person extends JavaBeanDeserializer {
public char[] zoo_asm_prefix__ = "\"zoo\":".toCharArray();
public char[] sex_asm_prefix__ = "\"sex\":".toCharArray();
public char[] beard_asm_prefix__ = "\"beard\":".toCharArray();
public ObjectDeserializer zoo_asm_deser__;
public ObjectDeserializer sex_asm_deser__;
public ObjectDeserializer beard_asm_deser__;
public FastjsonASMDeserializer_1_Person(ParserConfig var1, JavaBeanInfo var2) {
super(var1, var2);
}
public Object createInstance(DefaultJSONParser var1, Type var2) {
return new Person();
}
public Object deserialze(DefaultJSONParser var1, Type var2, Object var3, int var4) {
JSONLexerBase var5 = (JSONLexerBase)var1.lexer;
if (var5.token() == 14 && var5.isEnabled(var4, 8192)) {
return this.deserialzeArrayMapping(var1, var2, var3, (Object)null);
} else if (var5.isEnabled(512) && var5.scanType("com.feng.fastjson.bean.Person") != -1) {
Person var8;
int var12;
String var14;
String var15;
String var16;
label108: {
ParseContext var6 = var1.getContext();
int var7 = 0;
var8 = new Person();
ParseContext var9 = var1.getContext();
ParseContext var10 = var1.setContext(var9, var8, var3);
if (var5.matchStat != 4) {
boolean var11 = false;
var12 = 0;
boolean var13 = var5.isEnabled(4096);
String var10000;
if (var13) {
var12 |= 1;
var10000 = var5.stringDefaultValue();
} else {
var10000 = null;
}
var14 = (String)var10000;
if (var13) {
var12 |= 2;
var10000 = var5.stringDefaultValue();
} else {
var10000 = null;
}
var15 = (String)var10000;
if (var13) {
var12 |= 4;
var10000 = var5.stringDefaultValue();
} else {
var10000 = null;
}
var16 = (String)var10000;
var14 = var5.scanFieldString(this.beard_asm_prefix__);
if (var5.matchStat > 0) {
var12 |= 1;
}
if (var5.matchStat == -1) {
break label108;
}
label110: {
if (var5.matchStat > 0) {
++var7;
if (var5.matchStat == 4) {
break label110;
}
}
var15 = var5.scanFieldString(this.sex_asm_prefix__);
if (var5.matchStat > 0) {
var12 |= 2;
}
if (var5.matchStat == -1) {
break label108;
}
if (var5.matchStat > 0) {
++var7;
if (var5.matchStat == 4) {
break label110;
}
}
var16 = var5.scanFieldString(this.zoo_asm_prefix__);
if (var5.matchStat > 0) {
var12 |= 4;
}
if (var5.matchStat == -1) {
break label108;
}
if (var5.matchStat > 0) {
++var7;
if (var5.matchStat == 4) {
break label110;
}
}
if (var5.matchStat != 4) {
break label108;
}
}
if ((var12 & 1) != 0) {
var8.setBeard(var14);
}
if ((var12 & 2) != 0) {
var8.setSex(var15);
}
if ((var12 & 4) != 0) {
var8.setZoo(var16);
}
}
var1.setContext(var9);
if (var10 != null) {
var10.object = var8;
}
return var8;
}
if ((var12 & 1) != 0) {
var8.setBeard(var14);
}
if ((var12 & 2) != 0) {
var8.setSex(var15);
}
if ((var12 & 4) != 0) {
var8.setZoo(var16);
}
return (Person)this.parseRest(var1, var2, var3, var8, var4, new int[]{var12});
} else {
return super.deserialze(var1, var2, var3, var4);
}
}
public Object deserialzeArrayMapping(DefaultJSONParser var1, Type var2, Object var3, Object var4) {
JSONLexerBase var12 = (JSONLexerBase)var1.lexer;
String var5 = var12.scanTypeName(var1.getSymbolTable());
if (var5 != null) {
JavaBeanDeserializer var6 = JavaBeanDeserializer.getSeeAlso(var1.getConfig(), super.beanInfo, var5);
if (var6 instanceof JavaBeanDeserializer) {
return var6.deserialzeArrayMapping(var1, var2, var3, var12);
}
}
Person var7 = new Person();
String var8 = var12.scanString(',');
String var9 = var12.scanString(',');
String var10 = var12.scanString(']');
var7.setZoo(var10);
var7.setSex(var9);
var7.setBeard(var8);
char var11;
if ((var11 = var12.getCurrent()) == ',') {
var12.next();
var12.setToken(16);
} else if (var11 == ']') {
var12.next();
var12.setToken(15);
} else if (var11 == 26) {
var12.next();
var12.setToken(20);
} else {
var12.nextToken(16);
}
return var7;
}
}
可以看到label108起着顺序的作用,默认时通过json字符串的顺序反序列化的。顺序是可以控制的,即可以对属性field先排序再反序列化。
总结
在使用fastjson进行序列化与反序列化的过程中尽量使用JDK默认的set get方法,如果自定义get与set方法,建议控制顺序,避免序列化与反序列化过程中属性的丢失。
本文分析了FastJSON在反序列化过程中出现的部分属性丢失问题,对比了Gson和Jackson的表现,深入探讨了FastJSON源码,揭示了其反序列化顺序的影响,并提出了解决方案。

被折叠的 条评论
为什么被折叠?



