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

}
相关推荐
后端AI实验室4 小时前
我把一个生产Bug的排查过程,交给AI处理——20分钟后我关掉了它
java·ai
凉年技术6 小时前
Java 实现企业微信扫码登录
java·企业微信
狂奔小菜鸡7 小时前
Day41 | Java中的锁分类
java·后端·java ee
hooknum7 小时前
学习记录:基于JWT简单实现登录认证功能-demo
java
程序员Terry8 小时前
同事被深拷贝坑了3小时,我教他原型模式的正确打开方式
java·设计模式
NE_STOP8 小时前
MyBatis-缓存与注解式开发
java
码路飞8 小时前
不装 OpenClaw,我用 30 行 Python 搞了个 QQ AI 机器人
java
Re_zero8 小时前
以为用了 try-with-resources 就稳了?这三个底层漏洞让TCP双向通讯直接卡死
java·后端
SimonKing8 小时前
Fiddler抓包完全指南:从安装配置到抓包,一文讲透
java·后端·程序员
磊磊落落10 小时前
如何将 Spring Statemachine 作为一个轻量级工作流引擎来使用?
java