杰克逊前

使用Spring 5和Spring Boot 2开始,通过学习春天课程:

>>查看课程

1.概述

在本文中,我们将介绍杰克逊的课程层次结构。

两个典型的用例是包含子类型元数据和忽略从超类继承的属性。我们将描述这两种情况以及一些需要特殊处理子类型的情况。

2.包含子类型信息

在序列化和反序列化数据对象时,有两种方法可以添加类型信息,即全局默认类型和每个类的注释。

2.1。全局默认打字

以下三个Java类将用于说明全局包含类型元数据。

车辆超类:

public抽象类Vehicle {private String make;私人字符串模型;protected Vehicle(String make, String model){这个。让=让;这一点。模型=模型;} //无参数构造函数,getter和setter}

子类:

公共班车延伸车辆{私人int seatingcapacity;私人双最大速度;公共汽车(字符串制作,弦模型,int Seatupacity,Double Topspeed){超级(制作,型号);this.seatingcapacity = seatupacapacity;this.topspeed = topspeed;} //无参数构造函数,getter和setter}

卡车子类:

公共类卡车延长车辆{私人双重有效载荷;公共卡车(字符串制作,串模型,双重工资加载量){超级(制作,型号);this.payloadcapacity = payloadcapacity;} //无参数构造函数,getter和setter}

全局默认键入允许通过启用它来仅将键入一次信息objectmap目的。然后,类型元数据将应用于所有指定类型。结果,使用此方法添加类型元数据非常方便,尤其是当涉及大量类型时。缺点是它使用完全限定的Java类型名称作为类型标识符,因此不适合与非Java系统的交互,并且仅适用于多种预定义类型的类型。

车辆结构用于填充的实例舰队类:

公共类舰队{私人列表<车辆>车辆;// getters和setter}

要嵌入类型元数据,我们需要启用键入功能objectmap稍后将用于数据对象的序列化和反序列化的对象:

ObjectMapper.enableDefaulttyping(ObjectMapper.defaulttyping适用性,jsontypeinfo.as IncludeSeas)

适用性参数确定需要类型信息的类型,而包括参数是类型元数据包含的机制。另外,还有两种EnableDefaultty方法提供:

  • ObjectMapper.enableDefaultTyping(ObjectMapper.defaulttypitiving适用性):允许调用者指定适用性,而使用Wrapper_Array.作为默认值包括
  • ObjectMapper.enableDefaultTyping():使用object_and_non_concrete.作为默认值适用性Wrapper_Array.作为默认值包括

让我们看看它是如何工作的。首先,我们需要创建一个objectmap对象,并在其上启用默认类型:

objectapper = new objectapper ();mapper.enableDefaultTyping ();

下一步是实例化和填充此子部分开头引入的数据结构。要执行的代码将在后续子部分稍后重新使用。为方便起见并重新使用,我们会将其命名为车辆实例化块

Car =新车(“梅赛德斯-奔驰”,“S500”,5,250.0);卡车= new Truck("五十铃","NQR", 7500.0);List vehicles = new ArrayList<>();vehicles.add(车);vehicles.add(卡车);舰队serializedFleet = new舰队();serializedFleet.setVehicles(车辆);

这些填充的对象将被序列化:

jsonDataString = mapper. writevaleasstring (serializedFleet);

生成的json字符串:

{“车辆”:[“java.util.ArrayList”,[[“org.baeldung.jac金宝搏188体育kson.inheritance.car”,{“make”:“mercedes-benz”,“model”:“s500”,“seatupacacity”:5,“topspeed”:250.0}],[“org.baeldun金宝搏188体育g.jackson.inheritance.truck”,{“make”:“isuzu”,“型号”:“nqr”,“payloadcapacity”:7500.0}]]]}

在反序列化期间,从json字符串中恢复对象,其中类型数据保留:

舰队反序列化舰队=映射。readValue (jsonDataString Fleet.class);

重新创建的对象将与序列化之前的具体亚型相同:

assertthat(deserializedfleet.getvehiclesvehiclicle。get(0),instanceof(car.class));assertthat(deserializedfleet.getvehiclyhicles()。得到(1),instanceof(truck.class));

2.2.每个类的注释

每类注释是一种强大的方法,包括类型信息,对于需要显着的定制级别的复杂使用情况非常有用。但是,这只能以牺牲并发症为代价来实现。如果以两种方式配置类型信息,则每类注释覆盖全局默认键入。

要利用这种方法,SuperType应该注释@JsonTypeInfo和其他几个相关注释。本小节将使用类似于的数据模型车辆结构来说明每个类的注释。唯一的变化是添加了注释车辆抽象类,如下所示:

@JsonTypeInfo(use = JsonTypeInfo.Id。NAME, include = JsonTypeInfo.As。@JsonSubTypes({@Type(value = car .class, name = "car"), @Type(value = truck .class, name = "truck")}) public抽象类Vehicle{//字段,构造函数,getter和setter}

使用数据对象使用车辆实例化块在前一小节中引入,然后序列化:

jsonDataString = mapper. writevaleasstring (serializedFleet);

序列化产生以下JSON结构:

[{{“车辆”:“类型”:“车”、“让”:“梅赛德斯-奔驰”、“模型”:“准备”,“seatingCapacity”:5,最大速度:250.0},{“类型”:“车”、“让”:“五十铃”、“模型”:“NQR”、“payloadCapacity”:7500.0}]}

该字符串用于重新创建数据对象:

舰队反序列化舰队=映射。readValue (jsonDataString Fleet.class);

最后,验证了整个进度:

assertthat(deserializedfleet.getvehiclesvehiclicle。get(0),instanceof(car.class));assertthat(deserializedfleet.getvehiclyhicles()。得到(1),instanceof(truck.class));

3.忽略超类型的属性

有时,在序列化或反序列化期间需要从超额继承的一些属性。这可以通过三种方法中的一种来实现:注释,混合和注释内省。

3.1。注释

有两个常用的杰克逊注释来忽略属性,这是@jsonignore.@jsonignoreproperties..前者直接应用于类型成员,告诉Jackson在序列化或反序列化时忽略相应的属性。后者可用于任何级别,包括类型和类型成员,以列出应该忽略的属性。

@jsonignoreproperties.比另一个更强大,因为它允许我们忽略从我们无法控制的超类型继承的属性,比如外部库中的类型。此外,该注释允许我们一次忽略许多属性,在某些情况下,这可能导致代码更易于理解。

下面的类结构用来演示注释的用法:

public抽象类Vehicle {private String make;私人字符串模型;protected Vehicle(String make, String model){这个。让=让;这一点。模型=模型;@JsonIgnoreProperties({"model", "seatingCapacity"}) public抽象类Car extends Vehicle {private int seatingCapacity;@JsonIgnore private double topSpeed;protected Car(String make, String model, int seatingCapacity, double topSpeed) {super(make, model);this.seatingcapacity = seatupacapacity;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 }

如你所见,@jsonignore.告诉杰克逊忽略CAROPSPEED.财产,而且@jsonignoreproperties.忽略了Vehicle.modelCar.seatingCapacity那些。

两个注释的行为由以下测试验证。首先,我们需要实例化objectmap和数据类,然后使用它objectmap实例序列化数据对象:

objectapper = new objectapper ();轿车=新轿车(“梅赛德斯-奔驰”,“S500”,5,250.0);Crossover = new Crossover(“BMW”,“X6”,5,250.0,6000.0);List vehicles = new ArrayList<>();vehicles.add(轿车);vehicles.add(交叉);jsonDataString = mapper. writevaleasstring (vehicles);

jsonDataString包含以下JSON数组:

[{“制造”:“梅赛德斯-奔驰”},{“制造”:“宝马”、“towingCapacity”:6000.0}]

最后,我们将在生成的JSON字符串中证明存在或不存在各种属性名称:

为了(jsonDataString containsString(“做”));为了(jsonDataString,而不是(containsString(“模型”)));为了(jsonDataString,而不是(containsString(“seatingCapacity”)));为了(jsonDataString,而不是(containsString(最大速度)));assertthat(jsondatring,containstring(“towingcapacity”));

3.2.mix - in

MIX-INS允许我们应用行为(例如序列化和反序列化时忽略属性),无需直接将注释应用于类。在处理第三方类时,这尤其有用,在其中我们无法直接修改代码。

此子部分重用前一个引入的类继承链,除了@jsonignore.@jsonignoreproperties.答案在类已被移除:

public抽象类Car extends Vehicle {private int seatingCapacity;私人双最大速度;//字段,构造函数,getter和setter}

为了证明混合的操作,我们会忽略车辆CAROPSPEED.属性,然后使用测试确保一切正常工作。

第一步是声明一个混合类型:

private抽象类CarMixIn {@JsonIgnore public String make;@JsonIgnore public String topSpeed;}

接下来,混合绑定到数据类绑定到数据类objectmap对象:

objectapper = new objectapper ();mapper.addmixin(car.class,carmixin.class);

之后,我们将数据对象实例化并将它们序列化为字符串:

轿车=新轿车(“梅赛德斯-奔驰”,“S500”,5,250.0);Crossover = new Crossover(“BMW”,“X6”,5,250.0,6000.0);List vehicles = new ArrayList<>();vehicles.add(轿车);vehicles.add(交叉);jsonDataString = mapper. writevaleasstring (vehicles);

jsonDataString现在包含以下JSON:

[{“型号”:“S500”,“SeataPataPacity”:5},{“型号”:“X6”,“Seatupacity”:5,“TowingCapacity”:6000.0}]

最后,让我们验证结果:

为了(jsonDataString,而不是(containsString(“做”)));为了(jsonDataString containsString(“模型”));为了(jsonDataString containsString (seatingCapacity "));为了(jsonDataString,而不是(containsString(最大速度)));assertthat(jsondatring,containstring(“towingcapacity”));

3.3。注释自省

注释自省是忽略超类型属性的最强大的方法,因为它允许使用AnnotationIntrospector.hasignoremarker.API。

此子部分使用与前一体的相同类层次结构。在这个用例中,我们会问杰克逊忽略Vehicle.modelCrossover.Towapacity.的所有属性声明班级。让我们从一个扩展的类的声明开始杰克逊annotationintropector接口:

class IgnoranceIntrospector extends JacksonAnnotationIntrospector {public boolean hasIgnoreMarker(AnnotatedMember m) {return m. getdeclaringclass () == "model" || m. getdeclaringclass () == Car.class || m. getname () == "towingCapacity" || super.hasIgnoreMarker(m);}}

内省器将忽略与方法中定义的条件集匹配的任何属性(也就是说,它将把它们视为通过其他方法标记为忽略的属性)。

下一步是注册一个实例IgnoranceIntrospector类的objectmap对象:

objectapper = new objectapper ();映射器。setAnnotationIntrospector(新IgnoranceIntrospector ());

现在,我们使用与3.2节相同的方式创建和序列化数据对象。新生成的字符串的内容是:

[{“make”:“梅赛德斯 - 奔驰”},{“make”:“bmw”}]

最后,我们将验证introspector是否按照预期工作:

为了(jsonDataString containsString(“做”));为了(jsonDataString,而不是(containsString(“模型”)));为了(jsonDataString,而不是(containsString(“seatingCapacity”)));为了(jsonDataString,而不是(containsString(最大速度)));为了(jsonDataString,而不是(containsString(“towingCapacity”)));

4.亚型处理场景

本节将处理与子类处理相关的两个有趣的情景。

4.1。亚型之间的转换

Jackson允许将对象转换为原始对象之外的其他类型。事实上,这种转换可以发生在任何兼容的类型之间,但是在同一个接口或类的两个子类型之间使用这种转换最有帮助,以保护值和功能。

为了向另一个转换类型,我们将重复使用车辆从第2节取得的层次结构,增加了@jsonignore.诠释属性卡车避免不相容。

public class Car extends Vehicle {@JsonIgnore private int seatingCapacity;@JsonIgnore private double topSpeed;public class Truck extends Vehicle {@JsonIgnore private double payloadCapacity;//构造函数,getter和setter}

以下代码将验证转换成功,新对象是否保留了旧的对象的数据值:

objectapper = new objectapper ();Car =新车(“梅赛德斯-奔驰”,“S500”,5,250.0);卡车卡车= Mapper.ConvertValue(汽车,卡车。);assertequals(“梅赛德斯 - 奔驰”,truck.getmake());assertequals(“s500”,truck.getmodel());

4.2。没有NO-ARG构造函数的反序列化

默认情况下,Jackson使用No-Arg构造函数重新创建数据对象。在某些情况下,这是不方便的,例如当一个类具有非默认构造函数时,用户必须编写NO-ARG的才能满足杰克逊的要求。在类层次结构中,它更麻烦的是,必须将No-Arg构造函数添加到类中,并且继承链中的所有那些更高的构造函数。在这些情况下,创作者方法来救援。

此部分将使用与第2节中的一个类似的对象结构,对构造函数进行一些更改。具体而言,丢弃所有NO-ARG构造函数,并用混凝土亚型的构造函数@jsoncreator.@jsonproperty.使它们成为创建者方法。

公共班车延伸车辆{@JsonCreator公共汽车(@JsonProperty(“make”)字符串制作,@jsonproperty(“型号”)字符串模型,@jsonproperty(“座位”)int seatupacity,@jsonproperty(“topspeed”)Double Topspeed){超级(制作,型号);this.seatingcapacity = seatupacapacity;this.topspeed = topspeed;} //字段,Getters和Setter}公共类卡车扩展车辆{@JsonCreator公共卡车(@JsonProperty(“make”)String Make,@jsonproperty(“型号”)字符串模型,@jsonproperty(“有效载荷”)双重薪酬载流量){超级(制作,型号);this.payloadcapacity = payloadcapacity;} //字段,getter和setter}

一个测试将验证Jackson能够处理缺少无参数构造函数的对象:

objectapper = new objectapper ();mapper.enableDefaultTyping ();Car =新车(“梅赛德斯-奔驰”,“S500”,5,250.0);卡车= new Truck("五十铃","NQR", 7500.0);List vehicles = new ArrayList<>();vehicles.add(车);vehicles.add(卡车);舰队serializedFleet = new舰队();serializedFleet.setVehicles(车辆);jsonDataString = mapper. writevaleasstring (serializedFleet); mapper.readValue(jsonDataString, Fleet.class);

结论

本教程涵盖了几个有趣的用例,以演示杰克逊对类型继承的支持,重点是超型特性的多态性和无知。

可以找到所有这些示例和代码片段的实现一个github项目

杰克逊底部

使用Spring 5和Spring Boot 2开始,通过学习春天课程:

>>查看课程
这篇文章的评论已经关闭!