Jackson标签的高阶使用样例--多继承/子类、对象id、JsonIdentityInfo、JsonTypeInfo、JsonSubTypes

1. 背景

最近笔者在开发大数据平台XSailboat数据资产目录 模块。它的其中一个功能是能定义并查看资产数据。我们支持的资产类型不仅有关系数据库表,也支持Kafka主题,hdfs上的文件等。对于Kafka主题,hdfs文件等,它们没有强模式约束和描述,但为了理解、查看和约束其中的结构化数据,我们支持在这类资产上附加上模式定义的功能。

对于模式的描述/模型,我参考了JsonSchema,OpenAPI 3.0的Schema,最终决定自己定义模式结构。这种模式结构能扩展、能转换成那些标准模式,为后续扩展和适应性调整留余地。

我期望的模式结构,举例:

javascript 复制代码
{
	"type":"object" ,
	"fields":[{
		"name" : "f0" ,
		"description" : "字段0的描述信息" ,
		"dataType" : "string"
 	} ,
 	{
 		"name" : "f1" ,
 		"description" : "字段1的描述信息" ,
 		"dataType" : {
 			"type" : "array" ,
 			"itemType" : "string"
 		}
 	} ,
 	....
 	]
}

这里我对数据类型进行了抽闲,它可以是,

基本类型:

  • string
  • double
  • long
  • int
  • bool
  • datetime

扩展类型

  • object
  • array

基本类型在我们的Java Bean定义中,它是一个类型为BaseType的JavaBean,不是字符串,所以如何序列化成JSON时,得到

javascript 复制代码
{
...
	"dataType" : "string" ,
...
}

的效果,而不是,

javascript 复制代码
...
	"dataType" : ["BaseType" , "string"]
或者
	"dataType" : {
		"type" : "string"
	}
...

笔者经过尝试,使用了@JsonIdentityInfo和@JsonIdentityReference解决这个问题

2. 代码

  • com.cimstech.sailboat.common.schema.Type
java 复制代码
package com.cimstech.sailboat.common.schema;

import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

@JsonTypeInfo(
	    use = JsonTypeInfo.Id.NAME, // Were binding by providing a name
	    include = JsonTypeInfo.As.PROPERTY, // The name is provided in a property
	    property = "type", // Property name is type
	    visible = true // Retain the value of type after deserialisation
	    , defaultImpl = BaseType.class	// 如果不是下面指定的那两种,就缺省认定它是BaseType类型的,走它的反序列化逻辑。
	)
@JsonSubTypes({//Below, we define the names and the binding classes.
    @JsonSubTypes.Type(value = ArrayType.class, name = "array") ,
    @JsonSubTypes.Type(value = ObjectType.class, name = "object")
})
public interface Type
{
}
  • com.cimstech.sailboat.common.schema.BaseType
java 复制代码
package com.cimstech.sailboat.common.schema;

import com.cimstech.xfront.common.reflect.XClassUtil;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.JsonIdentityReference;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;

import lombok.Data;

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class , property = "name"
		, resolver = BaseTypeIdResolver.class , scope = BaseType.class)			// 用name来做id
@JsonIdentityReference(alwaysAsId = true)		// 总是使用Id来代表这个类的对象
@Data
public class BaseType implements Type
{
	public static BaseType STRING = new BaseType(XClassUtil.sCSN_String) ;
	public static BaseType LONG = new BaseType(XClassUtil.sCSN_Long) ;
	public static BaseType DOUBLE = new BaseType(XClassUtil.sCSN_Double) ;
	public static BaseType INTEGER = new BaseType(XClassUtil.sCSN_Integer) ;
	public static BaseType BOOLEAN = new BaseType(XClassUtil.sCSN_Bool) ;
	public static BaseType DATETIME = new BaseType(XClassUtil.sCSN_DateTime) ;
	
	public static BaseType of(String aTypeName)
	{
		switch(aTypeName)
		{
		case XClassUtil.sCSN_String:
			return STRING ;
		case XClassUtil.sCSN_Long:
			return LONG ;
		case XClassUtil.sCSN_Double:
			return DOUBLE ;
		case XClassUtil.sCSN_Integer:
			return INTEGER ;
		case XClassUtil.sCSN_Bool:
			return BOOLEAN ;
		case XClassUtil.sCSN_DateTime:
			return DATETIME ;
		default:
			throw new IllegalArgumentException("未知的基本类型:"+aTypeName) ;
		}
	}
	
	final String name ;
	
	private BaseType(String aName)
	{
		name = aName ;
	}
	
	@Override
	public String toString()
	{
		return name ;
	}
}
  • com.cimstech.sailboat.common.schema.ArrayType
java 复制代码
package com.cimstech.sailboat.common.schema;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ArrayType implements Type
{
	/**
	 * 数组的元素类型
	 */
	Type itemType ;
}
  • com.cimstech.sailboat.common.schema.ObjectType
java 复制代码
package com.cimstech.sailboat.common.schema;

import java.util.List;

import lombok.Data;

@Data
public class ObjectType implements Type
{
	/**
	 * 字段
	 */
	List<Field> fields ;
}
  • com.cimstech.sailboat.common.schema.Field
java 复制代码
package com.cimstech.sailboat.common.schema;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Field
{
	String name ;
	
	String description ;
	
	Type dataType ;
}
  • com.cimstech.sailboat.common.schema.BaseTypeIdResolver
java 复制代码
package com.cimstech.sailboat.common.schema;

import com.fasterxml.jackson.annotation.ObjectIdGenerator.IdKey;
import com.fasterxml.jackson.annotation.ObjectIdResolver;

public class BaseTypeIdResolver implements ObjectIdResolver
{
	
	@Override
	public void bindItem(IdKey aId, Object aPojo)
	{
	}

	@Override
	public Object resolveId(IdKey aId)
	{
		return BaseType.of(aId.key.toString()) ;
	}

	@Override
	public ObjectIdResolver newForDeserialization(Object aContext)
	{
		return this ;
	}

	@Override
	public boolean canUseFor(ObjectIdResolver aResolverType)
	{
		return aResolverType.getClass() == getClass() ;
	}

}
相关推荐
JH307328 分钟前
Java Stream API 在企业开发中的实战心得:高效、优雅的数据处理
java·开发语言·oracle
九月十九3 小时前
java使用aspose读取word里的图片
java·word
一 乐4 小时前
民宿|基于java的民宿推荐系统(源码+数据库+文档)
java·前端·数据库·vue.js·论文·源码
爱记录的小磊4 小时前
java-selenium自动化快速入门
java·selenium·自动化
鹏码纵横4 小时前
已解决:java.lang.ClassNotFoundException: com.mysql.jdbc.Driver 异常的正确解决方法,亲测有效!!!
java·python·mysql
weixin_985432114 小时前
Spring Boot 中的 @ConditionalOnBean 注解详解
java·spring boot·后端
Mr Aokey4 小时前
Java UDP套接字编程:高效实时通信的实战应用与核心类解析
java·java-ee
冬天vs不冷4 小时前
Java分层开发必知:PO、BO、DTO、VO、POJO概念详解
java·开发语言
hong_zc4 小时前
Java 文件操作与IO流
java·文件操作·io 流