Jackson使用@JsonTypeInfo反序列化多态类型(根据标识解析为子类对象)
2021/4/7 18:19:05
本文主要是介绍Jackson使用@JsonTypeInfo反序列化多态类型(根据标识解析为子类对象),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
问题场景
jackson可以将多态类型JSON序列化. 但在反序列化时会因为找不到具体的类而失败.
举例:创建4个POJO类
@Data public class AbstractTarget { } @Data @EqualsAndHashCode(callSuper = true) class HiveTarget extends AbstractTarget { private String schema; private String table; private String column; } @Data @EqualsAndHashCode(callSuper = true) class HBaseTarget extends AbstractTarget{ private String namespace; private String table; private String columnFamily; private String column; } @Data class Statistics { private List<AbstractTarget> targets; }
测试方法
@Test public void testDeserialize() throws JsonProcessingException { Statistics statistics = new Statistics(); List<AbstractTarget> targets = new ArrayList<>(); statistics.setTargets(targets); HiveTarget hiveTarget = new HiveTarget(); hiveTarget.setSchema("s1"); hiveTarget.setTable("t1"); hiveTarget.setColumn("c1"); targets.add(hiveTarget); HBaseTarget hBaseTarget= new HBaseTarget(); hBaseTarget.setNamespace("ns2"); hBaseTarget.setTable("t2"); hBaseTarget.setColumnFamily("cf2"); hBaseTarget.setColumn("c2"); targets.add(hBaseTarget); // 序列化 String statisticsStr = mapper.writeValueAsString(statistics); System.out.println(statisticsStr); // 反序列化 Statistics parsedStatistics = mapper.readValue(statisticsStr, Statistics.class); System.out.println(parsedStatistics); }
结果
{"targets":[{"schema":"s1","table":"t1","column":"c1"},{"namespace":"ns2","table":"t2","columnFamily":"cf2","column":"c2"}]} com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `fresh.json.AbstractTarget` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information at [Source: (String)"{"targets":[{"schema":"s1","table":"t1","column":"c1"},{"namespace":"ns2","table":"t2","columnFamily":"cf2","column":"c2"}]}"; line: 1, column: 13] (through reference chain: fresh.json.Statistics["targets"]->java.util.ArrayList[0]) ...
因此若要正确的反序列化,需要指定具体子类的标识。
方式一.使用类名作为标识
如下:使用类名作为标识符,并将标识符作为属性序列化,属性名称指定为"@class"。
@Data @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class" ) //等于@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS), 另外两个为类名作为标识的默认值 public class AbstractTarget { }
序列化结果中会包含”@class“属性,反序列化时就会根据”@class“找到具体的类。
{"targets":[{"@class":"fresh.json.HiveTarget","schema":"s1","table":"t1","column":"c1"},{"@class":"fresh.json.HBaseTarget","namespace":"ns2","table":"t2","columnFamily":"cf2","column":"c2"}]}
实测发现当直接使用List序列化时(targets)会丢失"@class"属性,嵌套的列表和单独对象都没有这个问题。
... System.out.println("serializing nested array-------"); System.out.println(mapper.writeValueAsString(statistics)); System.out.println("serializing object-------------"); System.out.println(mapper.writeValueAsString(hiveTarget)); System.out.println("serializing array--------------"); System.out.println(mapper.writeValueAsString(targets));
结果
serializing nested array------- {"targets":[{"@class":"fresh.json.HiveTarget","type":null,"schema":"s1","table":"t1","column":"c1"},{"@class":"fresh.json.HBaseTarget","type":null,"namespace":"ns2","table":"t2","columnFamily":"cf2","column":"c2"}]} serializing object------------- {"@class":"fresh.json.HiveTarget","type":null,"schema":"s1","table":"t1","column":"c1"} serializing array-------------- [{"type":null,"schema":"s1","table":"t1","column":"c1"},{"type":null,"namespace":"ns2","table":"t2","columnFamily":"cf2","column":"c2"}]
方式二.使用属性值作为标识
使用类名作为标识符的好处是配置方便,但是会在序列化中暴露类名,并且如果使用其他方式构造json串时可能需要手动设置”类名属性“。
另一种方式是使用属性值做为标识,配置较为繁琐,适合类中已经存在标识属性的情况。
如下:AbstractTarget存在type属性,并且在两个子类中设置了固定且不同的值,使用@JsonTypeInfo指定type属性作为”类标识“,同时需要使用@JsonSubTypes指定 具体类 和 type属性值 的关系。
@Data @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type" ) @JsonSubTypes({ @JsonSubTypes.Type(value = HiveTarget.class, name = HiveTarget.TYPE), @JsonSubTypes.Type(value = HBaseTarget.class, name = HBaseTarget.TYPE) }) public class AbstractTarget { private String type; } @Data @EqualsAndHashCode(callSuper = true) class HiveTarget extends AbstractTarget { private String schema; private String table; private String column; static final String TYPE = "hive"; public HiveTarget(){ setType(TYPE); } } @Data @EqualsAndHashCode(callSuper = true) class HBaseTarget extends AbstractTarget{ private String namespace; private String table; private String columnFamily; private String column; static final String TYPE = "hbase"; public HBaseTarget(){ setType(TYPE); } }
序列化结果就和普通的序列化一致,不会包含额外属性。
{"targets":[{"type":"hive","schema":"s1","table":"t1","column":"c1"},{"type":"hbase","namespace":"ns2","table":"t2","columnFamily":"cf2","column":"c2"}]}
Maven依赖
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.12.2</version> </dependency>
参考地址
Jackson里使用@JsonTypeInfo注解处理多态类型的序列化和反序列化
这篇关于Jackson使用@JsonTypeInfo反序列化多态类型(根据标识解析为子类对象)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-29如何在 Vuex 中设置,获取和遍历数据?-icode9专业技术文章分享
- 2024-11-28Vue-router课程:初学者快速入门指南
- 2024-11-28初学者指南:antdesignvue学习入门教程
- 2024-11-28Vue CLI资料入门教程
- 2024-11-28Vue CLI资料:新手入门指南
- 2024-11-28Threejs的三维坐标系
- 2024-11-27AntDesignVue入门指南:轻松搭建美观的Vue项目
- 2024-11-27Egg.js入门指南:新手必备的零基础教程
- 2024-11-27Hotkeys.js开发入门教程
- 2024-11-27Ant Design Vue入门指南:轻松搭建美观界面