FastJSON继承类反序列化参数

场景及问题

做工具封装时,通常会定义一个顶级父类data struct,子类继承之后补充自己独有的属性。该模式在常规使用情况下都是可用的,但是在涉及到序列化后反序列化对象时,就不是那么管用了。

我们假设有两个对象,class BaseClassclass 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功能,说不定就是需要的。/狗头