栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

JSON 多态反序列化属性/类型丢失问题

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

JSON 多态反序列化属性/类型丢失问题

一、背景

工作中有时候会遇到一个类定义了某个类型的父类作为成员变量,实际存放的为某个子类型, JSON 反序列化后,属性丢失的情况。

如果你赶时间,可以直接跳到第三部分看解决方案。

二、模拟

父类型

package json;

import lombok.Data;

@Data
public class Parent {
    private String name;
    private String sex ;
}

子类型1:

package json;

import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;

@EqualsAndHashCode(callSuper = true)
@Data
@ToString( callSuper = true)
public class Child extends Parent {
    private String c1Field;
}

子类型2:

package json;

import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;

@EqualsAndHashCode(callSuper = true)
@Data
@ToString( callSuper = true)
public class Child2 extends Parent{

    private String c2Filed;
}

演示类:

package json;

import lombok.Data;

@Data
public class Some {
    private Parent parent;
}

验证代码:

package json;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.core.JsonProcessingException;

public class ObjectDemo {
    public static void main(String[] args) throws JsonProcessingException {

        Some some = new Some();

        Child child = new Child();
        child.setName("张三");
        child.setSex("男");
        child.setC1Field("C1子类特有属性");

        some.setParent(child);

        String jsonStr = JSON.toJSONString(some);
        System.out.println("序列化后:"+jsonStr);

        Some result = JSON.parseObject(jsonStr,Some.class);
        System.out.println("反序列化后:"+result);
    }
}


执行结果:

序列化后:{"parent":{"c1Field":"C1子类特有属性","name":"张三","sex":"男"}}
反序列化后:Some(parent=Parent(name=张三, sex=男))

这样存在的问题:
1、有时候我们会依据 Parent 的具体子类型来执行不同的策略,由于无法确定类型,给我们的编码带来了困扰
2、反序列化时,由于无法感知序列化时 Parent 类的具体类型,反序列化丢失了 other 成员变量的值。

三、解决办法 3.1 将子类型写入 JSON 字符串

先说一个常规做法。

如果我们在序列化时将具体的子类型写入到 JSON 字符串中,反序列化时就可以使用该子类型对其进行反序列化。

以 fastjson 为例,可以在调用 JSON#toJSONString 时,指明写入类名。

package json;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.fasterxml.jackson.core.JsonProcessingException;

public class ObjectDemo {
    public static void main(String[] args) throws JsonProcessingException {

        Some some = new Some();

        Child child = new Child();
        child.setName("张三");
        child.setSex("男");
        child.setC1Field("C1子类特有属性");

        some.setParent(child);

        // 写入类名
        String jsonStr = JSON.toJSONString(some, SerializerFeature.WriteClassName);
        System.out.println("序列化后:"+jsonStr);

        Some result = JSON.parseObject(jsonStr,Some.class);
        System.out.println("反序列化后:"+result);

        // 判断子类型
        if(result.getParent() instanceof Child){
            System.out.println("执行 Child 子类对应的策略");
        }else
            if(result.getParent() instanceof Child2){
                System.out.println("执行 Child2 子类对应的策略");
            }
    }
}

运行结果

序列化后:{"@type":"json.Some","parent":{"@type":"json.Child","c1Field":"C1子类特有属性","name":"张三","sex":"男"}}
反序列化后:Some(parent=Child(super=Parent(name=张三, sex=男), c1Field=C1子类特有属性))
执行 Child 子类对应的策略

其他 JSON 序列化工具都有自己特定的方式,大家直接参考各自的官方文档即可。

这样做的缺点是和具体的序列化工具绑定,如果上下游用的不是同一套工具而且相互不兼容,就非常尴尬了!!

3.2 打平

为了不合具体的 JSON 序列化工具绑定,我们可以选择打平。
我个人更倾向于这种方式!

即如果 Parent 有多个了子类型,如果下游需要根据不同的子类型执行不同的策略。

我们可以将Parent 的子类型直接定义在 Some 类中。

package json;

import lombok.Data;

@Data
public class Some {
    private Child child;
    private Child2 child2;
}

这样反序列化不丢失属性,而且还可以根据子类型来执行不同策略。

package json;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.core.JsonProcessingException;

public class ObjectDemo {
    public static void main(String[] args) throws JsonProcessingException {

        Some some = new Some();

        Child child = new Child();
        child.setName("张三");
        child.setSex("男");
        child.setC1Field("C1子类特有属性");

        some.setChild(child);

        String jsonStr = JSON.toJSONString(some);
        System.out.println("序列化后:"+jsonStr);

        Some result = JSON.parseObject(jsonStr,Some.class);
        System.out.println("反序列化后:"+result);

        if(result.getChild()!= null){
            System.out.println("执行 Child 子类对应的策略");
        }
    }
}

结果:

序列化后:{"child":{"c1Field":"C1子类特有属性","name":"张三","sex":"男"}}
反序列化后:Some(child=Child(super=Parent(name=张三, sex=男), c1Field=C1子类特有属性), child2=null)
执行 Child 子类对应的策略
3.3 加入标记

如果我们不想将每个子类型都写入到 Some 类中,我们还可以在 Some 类中新增一个 String type 字段来标识具体是哪个子类型。

然后提供一个工厂方法即可。

package json;

public class ParentTypeFactory {

    public static  Class get(String type) {
        switch (type) {
            case "child1":
                return Child.class;
            case "child2":
                return Child2.class;
            default:
                return null;
        }
    }
}


覆写

package json;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.fasterxml.jackson.core.JsonProcessingException;

public class ObjectDemo {
    public static void main(String[] args) throws JsonProcessingException {

        Some some = new Some();

        Child child = new Child();
        child.setName("张三");
        child.setSex("男");
        child.setC1Field("C1子类特有属性");

        some.setParent(child);
        some.setType("child1");

        // 写入类名
        String jsonStr = JSON.toJSONString(some);
        System.out.println("序列化后:" + jsonStr);

        Some result = JSON.parseObject(jsonStr, Some.class);
        System.out.println("反序列化后:" + result);

        JSONObject jsonObject = JSON.parseObject(jsonStr);

        Parent parent = jsonObject.getObject("parent", ParentTypeFactory.get(result.getType()));
        result.setParent(parent);
        System.out.println("反序列化后并覆写parent:" + result);

        // 判断子类型
        if (result.getParent() instanceof Child) {
            System.out.println("执行 Child 子类对应的策略");
        } else if (result.getParent() instanceof Child2) {
            System.out.println("执行 Child2 子类对应的策略");
        }
    }
}

虽然这种方法需要新增一个类型的成员变量,但各种 JSON 框架都可以根据这个标识进行解析。

四、总结

本文主要讲 JSON 多态反序列化属性或类型丢失问题,并提供了几种解决方案,希望对大家有帮助。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/327688.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号