FastJson踩坑:@JsonField在反序列化时失效的解决
问题描述
⼀个对象(某个字段为枚举类型,为了不采⽤默认的序列化过程,⽤@JSONField指定了序列化器和反序列器,过程见旧博⽂),将其放到JSONArray中再序列化JSONArray对象,⽤得到的JSON字符串再反序列化时,发现能够正常反序列化出JSONArray,⽽对JSONArray中的某个元素再反序列化成类对象时,出错。
⽰例
同样⽤旧博⽂的⽰例做个简单测试。
基本对象类Article。
public class Article {
private String title;
private String content;
@JSONField(serializeUsing = AuditStatusCodec.class, deserializeUsing = AuditStatusCodec.class)
private AuditStatus status;
public Article(){
}
public Article(String title, String content, AuditStatus status){
this.title = title;
this.status = status;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
fastjson忽略属性return content;
}
public void setContent(String content) {
}
public AuditStatus getStatus() {
return status;
}
public void setStatus(AuditStatus status) {
this.status = status;
}
@Override
public String toString() {
return "Article{" +
"title='" + title + '\'' +
", content='" + content + '\'' +
", status=" + status +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o){
return true;
}
if (o == null || getClass() != o.getClass()){
return false;
}
Article article = (Article) o;
return Objects.equals(title, article.title) &&
Objects.equals(content, t) &&
status == article.status;
}
@Override
public int hashCode() {
return Objects.hash(title, content, status);
}
}
枚举类型AuditStatus。
public enum AuditStatus {
/
**
* 审核中
*/
AUDITING(1),
/**
* 通过
*/
PASSED(2),
/**
* 失败
*/
FAILED(3);
private int code;
AuditStatus(int code){
}
public int getCode() {
return code;
}
public static AuditStatus convert(int code){
AuditStatus[] enums = AuditStatus.values();
for(AuditStatus e : enums){
de == code){
return e;
}
}
return null;
}
}
以及序列化/反序列化器AuditStatusCodec
public class AuditStatusCodec implements ObjectSerializer, ObjectDeserializer {
@Override
public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
Object value = parser.parse();
return value == null ? (T) value : (T) vert(TypeUtils.castToInt(value));
}
@Override
public int getFastMatchToken() {
return 0;
}
@Override
public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
serializer.write(((AuditStatus)object).getCode());
}
}
按照出问题的情况,写模拟⽤例:
public class FastJsonTest {
@Test
public void deserializeTest(){
testJSONParse();
testJSONArrayParse();
}
protected static void testJSONParse(){
System.out.println("**************Start Test JSON Parse");
Article originalArticle = new Article("Article 1", "This is content", AuditStatus.AUDITING);
String jsonStr = JSONString(originalArticle);
System.out.println("Serialize to json string: " + jsonStr);
Article deserializeArticle = JSON.parseObject(jsonStr, Article.class);
System.out.println("Deserialize to Java Object: " + deserializeArticle + "; and the status is " + Status());
//Equals
Assert.Status().equals(AuditStatus.AUDITING));
Assert.assertEquals(originalArticle, deserializeArticle);
}
protected static void testJSONArrayParse(){
System.out.println("**************Start Test JSONArray Parse");
JSONArray arr = new JSONArray();
Article originArticle = new Article("Article 1", "This is content", AuditStatus.AUDITING);
arr.add(originArticle);
String jsonArrStr = JSONString(arr);
System.out.println("Serialize to json array string: " + jsonArrStr);
arr = JSON.parseArray(jsonArrStr);
Article deserializeArticle = Object(0, Article.class);
System.out.println("Deserialize to json arr, then to java object: " + deserializeArticle + "; ant the status is " + Status()); //Not Equals
Assert.Status().equals(AuditStatus.AUDITING));
Assert.assertNotEquals(originArticle, deserializeArticle);
}
}
看控制台输出的情况:
**************Start Test JSON Parse
Serialize to json string: {"content":"This is content","status":1,"title":"Article 1"}
Deserialize to Java Object: Article{title='Article 1', content='This is content', status=AUDITING}; and the status is
AUDITING
**************Start Test JSONArray Parse
Serialize to json array string: [{"content":"This is content","status":1,"title":"Article 1"}]
Deserialize to json arr, then to java object: Article{title='Article 1', content='This is content', status=PASSED}; and the
status is PASSED
上述代码中testJsonParse没有把类对象放到JSONArray中,可以从结果中看出序列化和反序列化过程均
正常。
⽽testJSONArrayParse先把类对象放到JSONArray中,在从JSONArray中取出对象反序列化,反序列化的结果就不正常了。
疑问
为什么JSONObject和JSONArray的反序列化过程得到的结果不⼀致?两者的反序列过程差异在哪?
DEBUG
遇事不决,开始DEBUG。
JSON.parseObject的流程
⾸先,JSON是⼀个门⾯类,提供出⼀些静态的⽅法供外部使⽤。⽐如说parseObject()⽅法。其内部会创建解析器DefaultJSONParser,并将解析委托给解析器执⾏。
DefualtJSONParser在创建时接受输⼊,全局配置及特性,相当于获取到了本次解析所有的数据。同时DefualtJSONParser的内部创建了⼀些⽤于解析的组件,例如JSONLexer(⽤于字符串解析)。解析过
程在parseObject中执⾏,parseObject会通过ParseConfig(保存解析配置的⼀个全局对象)获取到解析器ObjectDeserializer,并由解析器处理真正的解析过程。
在通过Class获取ObjectDeserializer时,⾸先会确定ParserConfig中是否缓存了对应的反序列化器,如果不存在,则会新建⼀个JavaBeanDeserializer(对于⼀般Java对象⽽⾔)。在新建过程中,会解析Class的属性,并保存在JavaBeanInfo中。解析器的解析过程就是对⽐JSON字符串中的KEY和JavaBeanInfo的过程,把对应的值反序列化出来(判断是否有JSONField注解,并根据注解的属性处理也在这⼀步),最终还原对象。
以流程图表⽰上述过程:
TypeUtils通过根据需要转型的类型从ParserConfig中获取ObjectDeserializer反序列化器,对于普通 Java Bean ⽽⾔,是JavaBeanDeserializer。
由于JSONArray中取出的Object实际上是JSONObject对象,因此会由JavaBeanDeserializer反序列化器的createInstance()⽅法执⾏反序列化,得到对象。
以流程图表⽰上述过程:
deserialize 和 createInstance 的不同
deserialize在反序列化时,会从class上获取更多的属性,其中就包括JSONField注解上的信息,⽽createInstance获取的信息较少,因此忽略JSONField所带的信息,导致⾃定义的反序列化器在反序列化时失效。
疑惑
为什么都是反序列化过程,⼆者在⾏为和表现上会有所不同?官⽅是如何定义deserialize和createInstance的?
上述这些问题还需要查询更多资料来明确。也希望了解缘由的读者进⾏告知。
问题的解决⽅式
解决的办法不先转换成JSONArray,然后再反序列化对象。⽽是通过JSON.parseArray直接转成对象的List。
以上为个⼈经验,希望能给⼤家⼀个参考,也希望⼤家多多⽀持。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论