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() ;
	}

}
相关推荐
xcl09255 小时前
幼儿托育系统开发实战:从需求分析到上线全流程指南
java·大数据·需求分析
吴声子夜歌5 小时前
ApacheCommons——commons-cli(命令行参数解析)
java·开发语言·apache
小小猪的春天5 小时前
Java 手写第一个 MCP Server:Spring AI MCP 半小时跑通
java·人工智能·spring boot·ai编程
find1star5 小时前
LeetCode 141:环形链表
java·算法·leetcode·链表
今天AI了吗6 小时前
DeepSeek Harness 深度解析:从评测架构到实战落地
java·网络·数据库·人工智能·架构·java-ee
许彰午6 小时前
27-AuthService登录链路
java·低代码·架构
泡海椒6 小时前
JQuick-Curl 性能分析:并发场景下的性能表现与调优,第三方接口调用不只要快写也要稳跑
java·开发语言·okhttp
Rain的Java大神之路7 小时前
JavaWeb开发如何解决跨域问题
java·前端·后端·nginx·web安全·面试·运维开发
自动化监测Learner7 小时前
Navicat Premium 17 中文版安装教程(2026最新版)
java·linux·数据库
那个松鼠很眼熟w7 小时前
4.Spring-Ai入门案例
java·人工智能·spring