Jackson是一个Java的用来处理JSON格式数据的类库,性能非常好。在项目开发中,使用Jackson库的@JsonTypeInfo和@JsonSubTypes注解实现Java多态解析,这里做一个记录。
一、需求背景
在项目开发中,model层的Channel类是一个多态的实现,Channel是一个抽象基类,其有两个子类DatasourceChannel和ServiceChannel。底层的数据库采用MongoDB,因此Channel的所有对象都是在一张表(collection)上的。这样的设计(多态思想)非常优雅,因为只需要面向接口(抽象类)编程而非面向实现(子类)编程。
- 序列化:Java对象转化为JSON格式数据(而后持久化在数据库中)
- 反序列化:通过JSON格式数据创建对应的Java对象
在默认的情况下,Java对象和序列化后的JSON格式数据的属性都是一样的。但是在Channel的多态实现下,需要具体的类型信息(子类)指导反序列化(因为两个子类的属性集是不同的)。通过Jackson库的@JsonTypeInfo与@JsonSubTypes注解,可以在序列化时,保存具体的类型信息到JSON数据中,当JSON反序列到Java对象时,就可以根据具体类型信息创建正确的Java对象。
- @JsonTypeInfo:指明类型信息字段
- @JsonSubTypes:指明不同类型信息分别对应的子类
二、源代码
1. 抽象基类Channel.java
@Data @NoArgsConstructor @Document(collection = "channel") @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "type") @JsonSubTypes({ @JsonSubTypes.Type(value = ServiceChannel.class, name = "Service"), @JsonSubTypes.Type(value = DatasourceChannel.class, name = "DataSource"), }) @Slf4j public abstract class Channel { @Id protected String id;
@NotBlank protected String ownerId;
protected List<TransformRule> transformRules;
protected FusionRule fusionRule;
protected String description;
abstract public ChannelDTO convert2DTO();
abstract void doDataDistribution(String message);
public abstract void doCompletion(); }
|
@JsonTypeInfo指明了类型信息字段为type,@JsonSubTypes指明子类ServiceChannel的类型信息为“Service”,子类DatasourceChannel的类型信息为“DataSource”。
类型信息字段type不属于Channel对象中的属性,它只存在于序列化后的JSON数据中。举个例子,一个普通的ServiceChannel对象序列化为JSON数据时,在JSON数据中会多出一个type属性:*”type” : “Service”*。如果从数据库中拿出这条JSON数据,反序列化为Java对象时,会遵循这个type字段的指导,按照子类ServiceChannel的属性集去构造对应的ServiceChannel对象,而构造成功的ServiceChannel对象中,不会出现该type字段。
2. 子类DatasourceChannel.java
@Data @NoArgsConstructor public class DatasourceChannel extends Channel{ @NotBlank private String datasourceId;
@Builder public DatasourceChannel(String id, String ownerId, List<TransformRule> transformRules, FusionRule fusionRule, String description, String datasourceId){ super.id = id; super.ownerId = ownerId; this.transformRules = transformRules; this.fusionRule = fusionRule; this.description = description; this.datasourceId = datasourceId; } }
|
3. 子类ServiceChannel.java
@Data @NoArgsConstructor @Slf4j public class ServiceChannel extends Channel{ @NotBlank private String serviceId;
private String serviceName;
@NotBlank private String apiId;
private String apiPath;
private HttpMethod apiMethod;
@NotBlank private String datasourceId;
@NotBlank private String processId;
@Builder public ServiceChannel(String id, String ownerId, String serviceId, String apiId, List<TransformRule> transformRules, FusionRule fusionRule, String description, String processId, String datasourceId){ super.id = id; super.ownerId = ownerId; this.serviceId = serviceId; this.apiId = apiId; this.transformRules = transformRules; this.fusionRule = fusionRule; this.description = description; this.processId = processId; this.datasourceId = datasourceId; } }
|