转自 胖哥:https://blog.csdn.net/cnhome/article/details/80030215

如果英文不好,可以使用谷歌浏览器的翻译查看该文章

1. Overview

In this article, we’ll have a look at working with class hierarchies in Jackson.

Two typical use cases are the inclusion of subtype metadata and ignoring properties inherited from superclasses. We’re going to describe those two scenarios and a couple of circumstances where special treatment of subtypes is needed.

2. Inclusion of Subtype Information

There are two ways to add type information when serializing and deserializing data objects, namely global default typing and per-class annotations.

2.1. Global Default Typing

The following three Java classes will be used to illustrate global inclusion of type metadata.

Vehicle superclass:

public abstract class Vehicle {private String make;private String model;protected Vehicle(String make, String model) {this.make = make;this.model = model;}// no-arg constructor, getters and setters
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

Car subclass:

public class Car extends Vehicle {private int seatingCapacity;private double topSpeed;public Car(String make, String model, int seatingCapacity, double topSpeed) {super(make, model);this.seatingCapacity = seatingCapacity;this.topSpeed = topSpeed;}// no-arg constructor, getters and setters
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

Truck subclass:

public class Truck extends Vehicle {private double payloadCapacity;public Truck(String make, String model, double payloadCapacity) {super(make, model);this.payloadCapacity = payloadCapacity;}// no-arg constructor, getters and setters
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

Global default typing allows type information to be declared just once by enabling it on an ObjectMapper object. That type metadata will then be applied to all designated types. As a result, it is very convenient to use this method for adding type metadata, especially when there are a large number of types involved. The downside is that it uses fully-qualified Java type names as type identifiers, and is thus unsuitable for interactions with non-Java systems, and is only applicable to several pre-defined kinds of types.

The Vehicle structure shown above is used to populate an instance of Fleet class:

public class Fleet {private List<Vehicle> vehicles;// getters and setters
}
  • 1
  • 2
  • 3
  • 4
  • 5

To embed type metadata, we need to enable the typing functionality on the ObjectMapper object that will be used for serialization and deserialization of data objects later on:

ObjectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping applicability, JsonTypeInfo.As includeAs)
  • 1

The applicability parameter determines the types requiring type information, and the includeAs parameter is the mechanism for type metadata inclusion. Additionally, two other variants of the enableDefaultTyping method are provided:

  • ObjectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping applicability): allows the caller to specify the applicability, while using WRAPPER_ARRAY as the default value for includeAs
  • ObjectMapper.enableDefaultTyping(): uses OBJECT_AND_NON_CONCRETE as the default value for applicability and WRAPPER_ARRAY as the default value for includeAs 
    Let’s see how it works. To begin, we need to create an ObjectMapper object and enable default typing on it:
ObjectMapper mapper=new ObjectMapper();
mapper.enableDefaultTyping();
  • 1
  • 2

The next step is to instantiate and populate the data structure introduced at the beginning of this sub-section. The code to do it will be re-used later on in the subsequent sub-sections. For the sake of convenience and re-use, we will name it the vehicle instantiation block.

Car car = new Car("Mercedes-Benz", "S500", 5, 250.0);
Truck truck = new Truck("Isuzu", "NQR", 7500.0);List<Vehicle> vehicles = new ArrayList<>();
vehicles.add(car);
vehicles.add(truck);Fleet serializedFleet = new Fleet();
serializedFleet.setVehicles(vehicles);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

Those populated objects will then be serialized:

String jsonDataString=mapper.writeValueAsString(serializedFleet);
  • 1

The resulting JSON string:

{"vehicles": ["java.util.ArrayList",[["org.baeldung.jackson.inheritance.Car",{"make": "Mercedes-Benz","model": "S500","seatingCapacity": 5,"topSpeed": 250.0}],["org.baeldung.jackson.inheritance.Truck",{"make": "Isuzu","model": "NQR","payloadCapacity": 7500.0}]]]}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

During deserialization, objects are recovered from the JSON string with type data preserved:

Fleet deserializedFleet = mapper.readValue(jsonDataString, Fleet.class);
  • 1

The recreated objects will be the same concrete subtypes as they were before serialization:

assertThat(deserializedFleet.getVehicles().get(0), instanceOf(Car.class));
assertThat(deserializedFleet.getVehicles().get(1), instanceOf(Truck.class));
  • 1
  • 2

2.2. Per-Class Annotations

Per-class annotation is a powerful method to include type information and can be very useful for complex use cases where a significant level of customization is necessary. However, this can only be achieved at the expense of complication. Per-class annotations override global default typing if type information is configured in both ways.

To make use of this method, the supertype should be annotated with @JsonTypeInfo and several other relevant annotations. This subsection will use a data model similar to the Vehicle structure in the previous example to illustrate per-class annotations. The only change is the addition of annotations on Vehicle abstract class, as shown below:

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({ @Type(value = Car.class, name = "car"), @Type(value = Truck.class, name = "truck")
})
public abstract class Vehicle {// fields, constructors, getters and setters
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

Data objects are created using the vehicle instantiation block introduced in the previous subsection, and then serialized:

String jsonDataString=mapper.writeValueAsString(serializedFleet);
  • 1

The serialization produces the following JSON structure:

{"vehicles": [{"type": "car","make": "Mercedes-Benz","model": "S500","seatingCapacity": 5,"topSpeed": 250.0},{"type": "truck","make": "Isuzu","model": "NQR","payloadCapacity": 7500.0}]}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

That string is used to re-create data objects:

Fleet deserializedFleet = mapper.readValue(jsonDataString, Fleet.class);
  • 1

Finally, the whole progress is validated:

assertThat(deserializedFleet.getVehicles().get(0), instanceOf(Car.class));
assertThat(deserializedFleet.getVehicles().get(1), instanceOf(Truck.class));
  • 1
  • 2

3. Ignoring Properties from a Supertype

Sometimes, some properties inherited from superclasses need to be ignored during serialization or deserialization. This can be achieved by one of three methods: annotations, mix-ins and annotation introspection.

3.1. Annotations

There are two commonly used Jackson annotations to ignore properties, which are @JsonIgnore and @JsonIgnoreProperties. The former is directly applied to type members, telling Jackson to ignore the corresponding property when serializing or deserializing. The latter is used at any level, including type and type member, to list properties that should be ignored.

@JsonIgnoreProperties is more powerful than the other since it allows us to ignore properties inherited from supertypes that we do not have control of, such as types in an external library. In addition, this annotation allows us to ignore many properties at once, which can lead to more understandable code in some cases.

The following class structure is used to demonstrate annotation usage:

public abstract class Vehicle {private String make;private String model;protected Vehicle(String make, String model) {this.make = make;this.model = model;}// no-arg constructor, getters and setters
}@JsonIgnoreProperties({ "model", "seatingCapacity" })
public abstract class Car extends Vehicle {private int seatingCapacity;@JsonIgnoreprivate double topSpeed;protected Car(String make, String model, int seatingCapacity, double topSpeed) {super(make, model);this.seatingCapacity = seatingCapacity;this.topSpeed = topSpeed;}// no-arg constructor, getters and setters
}public class Sedan extends Car {public Sedan(String make, String model, int seatingCapacity, double topSpeed) {super(make, model, seatingCapacity, topSpeed);}// no-arg constructor
}public class Crossover extends Car {private double towingCapacity;public Crossover(String make, String model, int seatingCapacity, double topSpeed, double towingCapacity) {super(make, model, seatingCapacity, topSpeed);this.towingCapacity = towingCapacity;}// no-arg constructor, getters and setters
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

As you can see, @JsonIgnore tells Jackson to ignore Car.topSpeed property, while @JsonIgnorePropertiesignores the Vehicle.model and Car.seatingCapacity ones.

The behavior of both annotations is validated by the following test. First, we need to instantiate ObjectMapperand data classes, then use that ObjectMapper instance to serialize data objects:

ObjectMapper mapper = new ObjectMapper();Sedan sedan = new Sedan("Mercedes-Benz", "S500", 5, 250.0);
Crossover crossover = new Crossover("BMW", "X6", 5, 250.0, 6000.0);List<Vehicle> vehicles = new ArrayList<>();
vehicles.add(sedan);
vehicles.add(crossover);String jsonDataString = mapper.writeValueAsString(vehicles);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

jsonDataString contains the following JSON array:

[{"make": "Mercedes-Benz"},{"make": "BMW","towingCapacity": 6000.0}
]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

Finally, we will prove the presence or absence of various property names in the resulting JSON string:

assertThat(jsonDataString, containsString("make"));
assertThat(jsonDataString, not(containsString("model")));
assertThat(jsonDataString, not(containsString("seatingCapacity")));
assertThat(jsonDataString, not(containsString("topSpeed")));
assertThat(jsonDataString, containsString("towingCapacity"));
  • 1
  • 2
  • 3
  • 4
  • 5

3.2. Mix-ins

Mix-ins allow us to apply behavior (such as ignoring properties when serializing and deserializing) without the need to directly apply annotations to a class. This is especially useful when dealing with third-party classes, in which we cannot modify the code directly.

This sub-section reuses the class inheritance chain introduced in the previous one, except that the @JsonIgnoreand @JsonIgnoreProperties annotations on the Car class have been removed:

public abstract class Car extends Vehicle {private int seatingCapacity;private double topSpeed;// fields, constructors, getters and setters
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

In order to demonstrate operations of mix-ins, we will ignore Vehicle.make and Car.topSpeed properties, then use a test to make sure everything works as expected.

The first step is to declare a mix-in type:

private abstract class CarMixIn {@JsonIgnorepublic String make;@JsonIgnorepublic String topSpeed;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Next, the mix-in is bound to a data class through an ObjectMapper object:

ObjectMapper mapper = new ObjectMapper();
mapper.addMixIn(Car.class, CarMixIn.class);
  • 1
  • 2

After that, we instantiate data objects and serialize them into a string:

Sedan sedan = new Sedan("Mercedes-Benz", "S500", 5, 250.0);
Crossover crossover = new Crossover("BMW", "X6", 5, 250.0, 6000.0);List<Vehicle> vehicles = new ArrayList<>();
vehicles.add(sedan);
vehicles.add(crossover);String jsonDataString = mapper.writeValueAsString(vehicles);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

jsonDataString now contains the following JSON:

[{"model": "S500","seatingCapacity": 5},{"model": "X6","seatingCapacity": 5,"towingCapacity": 6000.0}
]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

Finally, let’s verify the result:

assertThat(jsonDataString, not(containsString("make")));
assertThat(jsonDataString, containsString("model"));
assertThat(jsonDataString, containsString("seatingCapacity"));
assertThat(jsonDataString, not(containsString("topSpeed")));
assertThat(jsonDataString, containsString("towingCapacity"));
  • 1
  • 2
  • 3
  • 4
  • 5

3.3. Annotation Introspection

Annotation introspection is the most powerful method to ignore supertype properties since it allows for detailed customization using the AnnotationIntrospector.hasIgnoreMarker API.

This sub-section makes use of the same class hierarchy as the preceding one. In this use case, we will ask Jackson to ignore Vehicle.modelCrossover.towingCapacity and all properties declared in the Car class. Let’s start with the declaration of a class that extends the JacksonAnnotationIntrospector interface:

class IgnoranceIntrospector extends JacksonAnnotationIntrospector {public boolean hasIgnoreMarker(AnnotatedMember m) {return m.getDeclaringClass() == Vehicle.class && m.getName() == "model"|| m.getDeclaringClass() == Car.class|| m.getName() == "towingCapacity"|| super.hasIgnoreMarker(m);}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

The introspector will ignore any properties (that is, it will treat them as if they were marked as ignored via one of the other methods) that match the set of conditions defined in the method.

The next step is to register an instance of the IgnoranceIntrospector class with an ObjectMapper object:

ObjectMapper mapper = new ObjectMapper();
mapper.setAnnotationIntrospector(new IgnoranceIntrospector());
  • 1
  • 2

Now we create and serialize data objects in the same way as in section 3.2. The contents of the newly produced string are:

[{"make": "Mercedes-Benz"},{"make": "BMW"}
]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

Finally, we’ll verify that the introspector worked as intended:

assertThat(jsonDataString, containsString("make"));
assertThat(jsonDataString, not(containsString("model")));
assertThat(jsonDataString, not(containsString("seatingCapacity")));
assertThat(jsonDataString, not(containsString("topSpeed")));
assertThat(jsonDataString, not(containsString("towingCapacity")));
  • 1
  • 2
  • 3
  • 4
  • 5

4. Subtype Handling Scenarios

This section will deal with two interesting scenarios relevant to subclass handling.

4.1. Conversion Between Subtypes

Jackson allows an object to be converted to a type other than the original one. In fact, this conversion may happen among any compatible types, but it is most helpful when used between two subtypes of the same interface or class to secure values and functionality.

In order to demonstrate conversion of a type to another one, we will reuse the Vehicle hierarchy taken from section 2, with the addition of the @JsonIgnore annotation on properties in Car and Truck to avoid incompatibility.

public class Car extends Vehicle {@JsonIgnoreprivate int seatingCapacity;@JsonIgnoreprivate double topSpeed;// constructors, getters and setters
}public class Truck extends Vehicle {@JsonIgnoreprivate double payloadCapacity;// constructors, getters and setters
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

The following code will verify that a conversion is successful and that the new object preserves data values from the old one:

ObjectMapper mapper = new ObjectMapper();Car car = new Car("Mercedes-Benz", "S500", 5, 250.0);
Truck truck = mapper.convertValue(car, Truck.class);assertEquals("Mercedes-Benz", truck.getMake());
assertEquals("S500", truck.getModel());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

4.2. Deserialization Without No-arg Constructors

By default, Jackson recreates data objects by using no-arg constructors. This is inconvenient in some cases, such as when a class has non-default constructors and users have to write no-arg ones just to satisfy Jackson’s requirements. It is even more troublesome in a class hierarchy where a no-arg constructor must be added to a class and all those higher in the inheritance chain. In these cases, creator methods come to the rescue.

This section will use an object structure similar to the one in section 2, with some changes to constructors. Specifically, all no-arg constructors are dropped, and constructors of concrete subtypes are annotated with @JsonCreator and @JsonProperty to make them creator methods.

public class Car extends Vehicle {@JsonCreatorpublic Car(@JsonProperty("make") String make, @JsonProperty("model") String model, @JsonProperty("seating") int seatingCapacity, @JsonProperty("topSpeed") double topSpeed) {super(make, model);this.seatingCapacity = seatingCapacity;this.topSpeed = topSpeed;}// fields, getters and setters
}public class Truck extends Vehicle {@JsonCreatorpublic Truck(@JsonProperty("make") String make, @JsonProperty("model") String model, @JsonProperty("payload") double payloadCapacity) {super(make, model);this.payloadCapacity = payloadCapacity;}// fields, getters and setters
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

A test will verify that Jackson can deal with objects that lack no-arg constructors:

ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping();Car car = new Car("Mercedes-Benz", "S500", 5, 250.0);
Truck truck = new Truck("Isuzu", "NQR", 7500.0);List<Vehicle> vehicles = new ArrayList<>();
vehicles.add(car);
vehicles.add(truck);Fleet serializedFleet = new Fleet();
serializedFleet.setVehicles(vehicles);String jsonDataString = mapper.writeValueAsString(serializedFleet);
mapper.readValue(jsonDataString, Fleet.class);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

5. Conclusion

This tutorial has covered several interesting use cases to demonstrate Jackson’s support for type inheritance, with a focus on polymorphism and ignorance of supertype properties.

The implementation of all these examples and code snippets can be found in a GitHub project.

序列化对象互转--Jackson中的ObjectMapper,解决超类派生问题,序列化问题相关推荐

  1. 关于ActiveMQ序列化对象爆“Forbidden class xxx! ...”问题的解决

    如题所示,最开始使用了默认配置: <amq:connectionFactory id="amqConnectionFactory"brokerURL="tcp:// ...

  2. “error C2712: 无法在要求对象展开的函数中使用__try”解决办法

    VC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)https://blog.csdn.net/chenlycly/article/details/124272585C++软件异常排查从 ...

  3. Jackson使用:String 与对象互转、Jackson 从 json 字符串转换出对象

    前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程. 一.从json字符串转换出对象 Pager类: import com.fasterxml.jacks ...

  4. jackson中@JsonProperty、@JsonIgnore等常用注解总结

    最近用的比较多,把json相关的知识点都总结一下,jackjson的注解使用比较频繁, jackson的maven依赖 <dependency> <groupId>com.fa ...

  5. Java基础之序列化对象Serialized

    文章目录 序列化对象Serialized 目的: 序列化类型 应用场景 代码案例 直接应用 自定义对象序列化 序列化对象Serialized 目的: 序列化机制允许将实现序列化的Java对象转换成字节 ...

  6. oleVariant序列化对象

    midas支持使用OLEVARIANT序列化对象,最新的DATASNAP支持使用OLEVARAINT和JSON来序列化对象. 下面的代码演示OLEVARINAT序列化TPARAMS, TPARAMET ...

  7. java jackson maven,jackson中objectMapper的使用

    Jackson可以轻松的将Java对象转换成json对象和xml文档,同样也可以将json.xml转换成Java对象 ObjectMapper类是Jackson库的主要类.它称为ObjectMappe ...

  8. python self 序列_python中序列化对象

    # 序列化对象p1 import pickle                               # 内置库 class People: def __init__(self, name, a ...

  9. flutter 序列化_如何在Flutter中序列化对象

    flutter 序列化 If you intend to save user data to the shared preferences or local storage in your Flutt ...

  10. C++中序列化对象并存储到mysql

    1.序列化 C++序列化存在多种方式,我这里使用的boost,推荐看一个简单的教程. boost方法就是在类定义中添加一个友元类对象,并实现serialize()方法就可以让该类变为可序列化类.要使用 ...

最新文章

  1. 基于visual Studio2013解决面试题之0901奇偶站队
  2. linux怎么关闭iptables linux如何关闭防火墙
  3. 【图文】云栖大会深圳峰会:阿里云ET医疗大脑与工业大脑,机器学习平台PAI2.0...
  4. 硬核致敬Linux !30岁生日快乐!
  5. 360视频云Web前端HEVC播放器实践剖析
  6. C#小游戏-------猜数字(转载)
  7. centos 网卡状态
  8. 很多的Adobe Dreamweaver CS5序列号
  9. @Transactional注解下,Mybatis循环取序列的值,但得到的值都相同的问题
  10. Navicat Premium 15 激活后打开就会无响应,或者崩溃,自动退出,没有任何提示,有时候会说未响应
  11. iOS加速计和陀螺仪
  12. sqli-labs(50-53)
  13. python打开文件管理器
  14. word无法打开请去应用商店_爱不释手的PPT小工具,请收好
  15. 常用插接件2(DC 电源插头)
  16. ChatGPT中文网 - ChatGPT国内网页版在线使用
  17. office2021无法启动mircrosoft outlook 无法启动outlook视窗 怎么解决
  18. JDY-31 蓝牙模块使用(HC-06)
  19. 2022年电子产品行业现状
  20. ISE 工具下Flash芯片找不到时如何下载mcs文件

热门文章

  1. QQ2010登录协议分析-目前可取得sessionkey
  2. 运动如何影响肠道微生物群,运动期间改善肠道问题的饮食建议
  3. 五种“网络钓鱼”实例解析及防范技巧(转)
  4. RPL基础知识点与组网过程
  5. const char *p与char * const p区别
  6. html水平线变虚线,html水平线 虚线
  7. 计算机网络技术在实践中应用,计算机网络技术及在实践中的具体应用
  8. 微信付款为什么无法连接服务器,前台微信付款报错:无法连接服务器,请检查网络连接?...
  9. 小白刷LeeCode(算法篇)6
  10. 苏宁易购执行总裁任峻在IT体系年会上的讲话