Have a look at working with class hierarchies in Jackson.
Jackson如何处理类中类
There are two ways to add type information when serializing and deserializing data objects, namely global default typing
and per-class annotations
.
@Data
@AllArgsConstructor
@NoArgsConstructor
public abstract class Vehicle {
private String make;
private String model;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
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;
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Truck extends Vehicle {
private double payloadCapacity;
public Truck(String make, String model, double payloadCapacity) {
super(make, model);
this.payloadCapacity = payloadCapacity;
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Fleet {
private List<Vehicle> vehicles;
}
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);
安全白名单
PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder()
.allowIfSubType("javabasic.enumprac")
.allowIfSubType("java.util.ArrayList")
.build();
ObjectMapper mapper = new ObjectMapper();
mapper.activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.NON_FINAL);
String jsonDataString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(serializedFleet);
System.out.println(jsonDataString);
[ "javabasic.enumprac.Fleet", {
"vehicles" : [ "java.util.ArrayList", [ [ "javabasic.enumprac.Car", {
"make" : "Mercedes-Benz",
"model" : "S500",
"seatingCapacity" : 5,
"topSpeed" : 250.0
} ], [ "javabasic.enumprac.Truck", {
"make" : "Isuzu",
"model" : "NQR",
"payloadCapacity" : 7500.0
} ] ] ]
} ]
String JSON= "[ \"javabasic.enumprac.Fleet\", {\n" +
" \"vehicles\" : [ \"java.util.ArrayList\", [ [ \"javabasic.enumprac.Car\", {\n" +
" \"make\" : \"Mercedes-Benz\",\n" +
" \"model\" : \"S500\",\n" +
" \"seatingCapacity\" : 5,\n" +
" \"topSpeed\" : 250.0\n" +
" } ], [ \"javabasic.enumprac.Truck\", {\n" +
" \"make\" : \"Isuzu\",\n" +
" \"model\" : \"NQR\",\n" +
" \"payloadCapacity\" : 7500.0\n" +
" } ] ] ]\n" +
"} ]";
Fleet deserializedFleet = mapper.readValue(JSON, Fleet.class);
System.out.println(deserializedFleet);
// Fleet(vehicles=[Car(seatingCapacity=5, topSpeed=250.0), Truck(payloadCapacity=7500.0)])
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = Car.class, name = "car"),
@JsonSubTypes.Type(value = Truck.class, name = "truck")
})
@Data
@AllArgsConstructor
@NoArgsConstructor
public abstract class Vehicle {
private String make;
private String model;
}
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);
ObjectMapper mapper = new ObjectMapper();
String jsonDataString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(serializedFleet);
System.out.println(jsonDataString);
{
"vehicles" : [ {
"type" : "car",
"make" : "Mercedes-Benz",
"model" : "S500",
"seatingCapacity" : 5,
"topSpeed" : 250.0
}, {
"type" : "truck",
"make" : "Isuzu",
"model" : "NQR",
"payloadCapacity" : 7500.0
} ]
}
Fleet deserializedFleet = mapper.readValue(jsonDataString, Fleet.class);
System.out.println(deserializedFleet);
// Fleet(vehicles=[Car(seatingCapacity=5, topSpeed=250.0), Truck(payloadCapacity=7500.0)])
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
.
有两种常用的Jackson注释来忽略属性,它们是@JsonIgnore
和@JsonIgnoreProperties
。
@JsonIgnore
directly applied to type members.
@JsonIgnoreProperties
applied to type and type member.
@JsonIgnoreProperties
is more powerful
1、it can ignore properties inherited from supertypes that we do not have control of.
2、it can ignore many properties at once.
@Data
@AllArgsConstructor
@NoArgsConstructor
public abstract class Vehicle {
private String make;
private String model;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@JsonIgnoreProperties({ "model", "seatingCapacity" })
public class Car extends Vehicle {
private int seatingCapacity;
@JsonIgnore
private double topSpeed;
public Car(String make, String model, int seatingCapacity, double topSpeed) {
super(make, model);
this.seatingCapacity = seatingCapacity;
this.topSpeed = topSpeed;
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
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;
}
}
@Data
@AllArgsConstructor
public class Sedan extends Car {
public Sedan(String make, String model, int seatingCapacity, double topSpeed) {
super(make, model, seatingCapacity, topSpeed);
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Fleet {
private List<Vehicle> vehicles;
}
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);
ObjectMapper mapper = new ObjectMapper();
String jsonDataString = mapper.writeValueAsString(vehicles);
System.out.println(jsonDataString);
[{"make":"Mercedes-Benz"},{"make":"BMW","towingCapacity":6000.0}]
Mix-ins allow us to 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
@Data
@AllArgsConstructor
@NoArgsConstructor
public abstract class Vehicle {
private String make;
private String model;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
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;
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
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;
}
}
@Data
@AllArgsConstructor
public class Sedan extends Car {
public Sedan(String make, String model, int seatingCapacity, double topSpeed) {
super(make, model, seatingCapacity, topSpeed);
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Fleet {
private List<Vehicle> vehicles;
}
public static void main(String[] args) throws JsonProcessingException {
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);
ObjectMapper mapper = new ObjectMapper();
mapper.addMixIn(Car.class, CarMixIn.class);
String jsonDataString = mapper.writeValueAsString(vehicles);
System.out.println(jsonDataString);
}
private abstract class CarMixIn {
@JsonIgnore
public String make;
@JsonIgnore
public String topSpeed;
}
原始值:
[{"make":"Mercedes-Benz","model":"S500","seatingCapacity":5,"topSpeed":250.0},{"make":"BMW","model":"X6","seatingCapacity":5,"topSpeed":250.0,"towingCapacity":6000.0}]
ignore以后得值
[{"model":"S500","seatingCapacity":5},{"model":"X6","seatingCapacity":5,"towingCapacity":6000.0}]
the most powerful method to ignore supertype properties since it allows for detailed customization using the AnnotationIntrospector.
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);
}
}
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);
ObjectMapper mapper = new ObjectMapper();
mapper.setAnnotationIntrospector(new IgnoranceIntrospector());
String jsonDataString = mapper.writeValueAsString(vehicles);
System.out.println(jsonDataString);
// [{"make":"Mercedes-Benz"},{"make":"BMW"}]
Jackson allows an object to be converted to aother type object.
it is most helpful when used between two subtypes of the same interface or class to secure values and functionality.
@Data
@AllArgsConstructor
@NoArgsConstructor
public abstract class Vehicle {
private String make;
private String model;
}
在Car和Truck的属性上添加@JsonIgnore注释以避免不兼容
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Car extends Vehicle {
@JsonIgnore
private int seatingCapacity;
@JsonIgnore
private double topSpeed;
public Car(String make, String model, int seatingCapacity, double topSpeed) {
super(make, model);
this.seatingCapacity = seatingCapacity;
this.topSpeed = topSpeed;
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Truck extends Vehicle {
@JsonIgnore
private double payloadCapacity;
public Truck(String make, String model, double payloadCapacity) {
super(make, model);
this.payloadCapacity = payloadCapacity;
}
}
Car car = new Car("Benz", "S500", 5, 250.0);
Truck truck1 = new Truck("Isuzu", "NQR", 7500.0);
ObjectMapper mapper = new ObjectMapper();
String s = mapper.writeValueAsString(car);
System.out.println(s);
String s1 = mapper.writeValueAsString(truck1);
System.out.println(s1);
Truck truck = mapper.convertValue(car, Truck.class);
System.out.println(truck);
{"make":"Benz","model":"S500"}
{"make":"Isuzu","model":"NQR"}
Truck(payloadCapacity=0.0)
默认情况下,Jackson通过使用无参数构造函数来重新创建数据对象。
By default, Jackson recreates data objects by using no-arg constructors.
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.
Specifically, all no-arg constructors are dropped, and constructors of concrete subtypes are annotated with @JsonCreator
and @JsonProperty
to make them creator methods.
@Data
@AllArgsConstructor
@NoArgsConstructor
public abstract class Vehicle {
private String make;
private String model;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Car extends Vehicle {
private int seatingCapacity;
private double topSpeed;
@JsonCreator
public 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;
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Truck extends Vehicle {
private double payloadCapacity;
@JsonCreator
public Truck(
@JsonProperty("make") String make,
@JsonProperty("model") String model,
@JsonProperty("payload") double payloadCapacity) {
super(make, model);
this.payloadCapacity = payloadCapacity;
}
}
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);
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping();
String jsonDataString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(serializedFleet);
System.out.println(jsonDataString);
Fleet fleet = mapper.readValue(jsonDataString, Fleet.class);
System.out.println(fleet);
{
"vehicles" : [ "java.util.ArrayList", [ [ "javabasic.enumprac.prac.Car", {
"make" : "Mercedes-Benz",
"model" : "S500",
"topSpeed" : 250.0,
"seatingCapacity" : 5
} ], [ "javabasic.enumprac.prac.Truck", {
"make" : "Isuzu",
"model" : "NQR",
"payloadCapacity" : 7500.0
} ] ] ]
}
Fleet(vehicles=[Car(seatingCapacity=5, topSpeed=250.0), Truck(payloadCapacity=7500.0)])
-----------------------------------------------------------------------------读书笔记摘自 文章:Inheritance with Jackson