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

1. 背景

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

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

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

{
	"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时,得到

{
...
	"dataType" : "string" ,
...
}

的效果,而不是,

...
	"dataType" : ["BaseType" , "string"]
或者
	"dataType" : {
		"type" : "string"
	}
...

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

2. 代码

  • com.cimstech.sailboat.common.schema.Type
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
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
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
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
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
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() ;
	}

}

  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[1\]中提到了如何手动排除一些不需要的依赖,并引入其他依赖。如果你使用了mp-plus的依赖,但无法访问org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer类文件,可能是因为该类文件在你的项目中无法找到。这可能是由于依赖冲突或版本不兼容导致的。 为了解决这个问题,你可以尝试以下几个步骤: 1. 确保你的项目中已经正确引入了spring-boot-starter-web模块,可以在pom.xml文件中查看是否有以下依赖配置: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> ``` 这个模块提供了许多开箱即用的依赖,包括Jackson相关的依赖。 2. 检查你的mp-plus依赖是否与其他依赖存在冲突。你可以尝试在pom.xml文件中排除一些可能引起冲突的依赖,例如: ```xml <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </exclusion> </exclusions> </dependency> ``` 这样可以排除mp-plus中可能与spring-boot-starter-web冲突的依赖。 3. 确保你的项目中的所有依赖版本兼容。你可以尝试更新mp-plus和其他相关依赖的版本,以确保它们与spring-boot-starter-web的版本兼容。 如果以上步骤都没有解决你的问题,你可能需要进一步检查你的项目配置和依赖关系,或者查看相关的错误日志以获取更多信息。 #### 引用[.reference_title] - *1* *2* *3* [1.开启Spring Boot](https://blog.csdn.net/MissOfSpring/article/details/100140156)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值