场景及问题
做工具封装时,通常会定义一个顶级父类data struct,子类继承之后补充自己独有的属性。该模式在常规使用情况下都是可用的,但是在涉及到序列化后反序列化对象时,就不是那么管用了。
我们假设有两个对象,class BaseClass
和 class ChildClass
import lombok.Data;
/**
* @author imyzt
* @date 2020/04/25
* @description 超类
*/
@Data
public class BaseClass {
private Integer bashInfo;
}
import lombok.*;
/**
* @author imyzt
* @date 2020/04/25
* @description 子类
*/
@EqualsAndHashCode(callSuper = true)
@Data
@ToString(callSuper = true)
@AllArgsConstructor
@NoArgsConstructor
public class ChildClass extends BaseClass {
private String childInfo;
}
我们假设生产端生产消息,通过FastJSON序列化为字符串,然后通过kafka或者其他途径传递到消费端,消费端通过超类BaseClass
转换消息,根据不同的子类通过不同的策略去处理。但是在这种情况下,通过超类解析字符串对象回JavaBean对象时,一般只能将超类中拥有的参数转换回来,不能获得子类的属性。示例代码如下:
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
/**
* @author imyzt
* @date 2020/04/25
* @description test
*/
@Slf4j
public class Test {
public static void main(String[] args) {
ChildClass one = new ChildClass();
one.setBashInfo(122);
one.setChildInfo("childInfo");
// 转换为字符串, 便于传输
String str = parseStr(one);
log.info("str={}", str);
// 转换回java bean
BaseClass childClass = JSON.parseObject(str, BaseClass.class);
log.info("childClass={}", childClass);
}
private static String parseStr(BaseClass obj) {
return JSON.toJSONString(obj);
}
}
日志打印:
14:18:09.208 [main] INFO xxx.json.Test - str={"bashInfo":122,"childInfo":"childInfo"}
14:18:09.227 [main] INFO xxx.json.Test - childClass=BaseClass(bashInfo=122)
实现方式
遇到这种情况,我们可以通过JSON的自省功能,实现在反序列化为JavaBean时,得到完整的子类属性,并且将JavaBean通过多态得到对应的子类对象。具体思路就是在序列化为JSON时,在json字符串中写入对应的类型,在反序列化时,通过JSON字符串中特殊标记的类型属性,按照该类型进行反序列化即可。
FastJSON具体的实现是在调用 JSON.toJSONString()
时,第二个数组参数传入Feature功能SerializerFeature.WriteClassName
,完整调用如JSON.toJSONString(obj, SerializerFeature.WriteClassName);
在写入为JSON字符串时,会带有一个特殊的标记@type
存储序列化时对应的类型,如下图。
在反序列化时,只需要按照对应的类型进行解析即可。上述代码重新运行后日志如下:
14:22:24.528 [main] INFO xxx.json.Test - str={"@type":"xxx.json.ChildClass","bashInfo":122,"childInfo":"childInfo"}
14:25:03.631 [main] INFO xxx.json.Test - childClass=ChildClass(super=BaseClass(bashInfo=122), childInfo=childInfo)
注意点
- 子类必须实现全参构造,或者有用lombok的
@ToString(callSuper = true)
也行,不然会出现映射父类属性的情况。
OK, 整篇文章就讲了一个Feature,水了一整页。。可以多关注下JSON的其他Feature功能,说不定就是需要的。/狗头