如何使用 Jackson 和 Spring Boot 提供自定义解串器
How to provide a custom deserializer with Jackson and Spring Boot
我有以下三个应用程序:
Project 1 holds
- 业务逻辑(Spring 云函数)
- 一个接口IDemoEntity
Project 2
- AWS 特定处理程序
- IDemoEntity 的一个实现,带有特定于 DynamoDB 的注释
- 该项目基于Spring Boot
Project 3
- IDemoEntity 的一个实现,带有 CosmosDB 注释
- Azure 特定处理程序
项目 1 的 类 如下所示:
public interface IDemoEntity {
String getName();
void setName(String name);
}
@Component
public class StoreFunction implements Consumer<Message<IDemoEntity>> {
@Override
public void accept(Message<IDemoEntity> t) {
System.out.println("Stored entity " + t.getPayload().getName());
return;
}
}
对于项目 2,IDemoEntity 的实现如下所示:
@DynamoDBTable(tableName = "DemoEntity")
public class DynamoDemoEntity implements IDemoEntity {
private String name;
@Override
@DynamoDBHashKey
public String getName() {
return name;
}
@Override
public void setName(String name) {
this.name = name;
}
}
对于项目 3,IDemoEntity 的实现看起来类似于 DynamoDemoEntity,但带有 CosmosDB 注释。
结构可能看起来有点复杂,但思路如下:
- 实施一次业务逻辑和数据模型(在项目 1 中)(利用 Spring Cloud Function)
- 只为每个平台实现一个包装器项目(我从项目 2 中的 AWS Lambda 开始,但 Azure 的项目 3 看起来很相似),以及特定于平台的东西(比如实体实现,这需要数据库-特定注释)
- 以项目 1 作为依赖项编译特定于平台的项目(例如 AWS Lambda 的项目 2)
我试过了,设置基本可以。
但是,有一个大问题:
当调用上面的 StoreFunction 时,Jackson 抛出以下异常:
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `de.margul.awstutorials.springcloudfunction.logic.IDemoEntity` (no Creators, like default construct, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
at [Source: (String)"{"name": "Detlef"}"; line: 1, column: 1]
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1452)
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1028)
at com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserialize(AbstractDeserializer.java:265)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4013)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3004)
at de.margul.awstutorials.springcloudfunction.aws.handler.RestSpringBootApiGatewayRequestHandler.deserializeBody(RestSpringBootApiGatewayRequestHandler.java:57)
... 3 more
这是有道理的,因为 Jackson 不知道,如果 IDemoEntity
它将接收到的 JSON.
反序列化到哪个实现
现在最简单的方法是在 IDemoEntity
上放置一个 @JsonDeserialize(as = DynamoDemoEntity.class)
。
但是,这会破坏我的完整结构:项目 1 应该没有信息,它是用哪个平台特定的项目编译的。
任何想法,我如何提供自定义反序列化器(例如 Spring bean),但不在项目 1 中进行特定于平台的修改?
首先,您需要创建自定义 DynamoDemoEntityDeserializer
,如下所示:
class DynamoDemoEntityDeserializer extends JsonDeserializer<DynamoDemoEntity> {
@Override
public DynamoDemoEntity deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
// return DynamoDemoEntity instance;
}
}
然后你可以像下面这样创建 com.fasterxml.jackson.databind.Module
的 bean:
@Bean
public Module dynamoDemoEntityDeserializer() {
SimpleModule module = new SimpleModule();
module.addDeserializer(IDemoEntity.class, new DynamoDemoEntityDeserializer());
return module;
}
Any beans of type com.fasterxml.jackson.databind.Module are automatically registered with the auto-configured Jackson2ObjectMapperBuilder and are applied to any ObjectMapper instances that it creates. This provides a global mechanism for contributing custom modules when you add new features to your application.
如果您想将给定对象反序列化为任何实体,那么您需要为此编写自己的自定义方法。
这是我用过的一个示例,它适用于任何实体。请看看并尝试一下。
@Service
public class ObjectSerializer {
private static final Logger logger = LoggerFactory.getLogger(ObjectSerializer.class);
private static ObjectMapper objectMapper;
@Autowired
private ObjectSerializer(ObjectMapper objectMapper) {
ObjectSerializer.objectMapper = objectMapper;
}
public static <T> T getObject(Object obj, Class<T> class1) {
String jsonObj = "";
T objectDto = null;
try {
jsonObj = objectMapper.writeValueAsString(obj);
objectDto = objectMapper.readValue(jsonObj, class1);
}catch (IOException e) {
logger.error("Exception Occured in ObjectSerializer : | {}",e.getMessage());
}
return objectDto;
}
希望对您有所帮助。
我有以下三个应用程序:
Project 1 holds
- 业务逻辑(Spring 云函数)
- 一个接口IDemoEntity
Project 2
- AWS 特定处理程序
- IDemoEntity 的一个实现,带有特定于 DynamoDB 的注释
- 该项目基于Spring Boot
Project 3
- IDemoEntity 的一个实现,带有 CosmosDB 注释
- Azure 特定处理程序
项目 1 的 类 如下所示:
public interface IDemoEntity {
String getName();
void setName(String name);
}
@Component
public class StoreFunction implements Consumer<Message<IDemoEntity>> {
@Override
public void accept(Message<IDemoEntity> t) {
System.out.println("Stored entity " + t.getPayload().getName());
return;
}
}
对于项目 2,IDemoEntity 的实现如下所示:
@DynamoDBTable(tableName = "DemoEntity")
public class DynamoDemoEntity implements IDemoEntity {
private String name;
@Override
@DynamoDBHashKey
public String getName() {
return name;
}
@Override
public void setName(String name) {
this.name = name;
}
}
对于项目 3,IDemoEntity 的实现看起来类似于 DynamoDemoEntity,但带有 CosmosDB 注释。
结构可能看起来有点复杂,但思路如下:
- 实施一次业务逻辑和数据模型(在项目 1 中)(利用 Spring Cloud Function)
- 只为每个平台实现一个包装器项目(我从项目 2 中的 AWS Lambda 开始,但 Azure 的项目 3 看起来很相似),以及特定于平台的东西(比如实体实现,这需要数据库-特定注释)
- 以项目 1 作为依赖项编译特定于平台的项目(例如 AWS Lambda 的项目 2)
我试过了,设置基本可以。 但是,有一个大问题:
当调用上面的 StoreFunction 时,Jackson 抛出以下异常:
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `de.margul.awstutorials.springcloudfunction.logic.IDemoEntity` (no Creators, like default construct, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
at [Source: (String)"{"name": "Detlef"}"; line: 1, column: 1]
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1452)
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1028)
at com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserialize(AbstractDeserializer.java:265)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4013)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3004)
at de.margul.awstutorials.springcloudfunction.aws.handler.RestSpringBootApiGatewayRequestHandler.deserializeBody(RestSpringBootApiGatewayRequestHandler.java:57)
... 3 more
这是有道理的,因为 Jackson 不知道,如果 IDemoEntity
它将接收到的 JSON.
现在最简单的方法是在 IDemoEntity
上放置一个 @JsonDeserialize(as = DynamoDemoEntity.class)
。
但是,这会破坏我的完整结构:项目 1 应该没有信息,它是用哪个平台特定的项目编译的。
任何想法,我如何提供自定义反序列化器(例如 Spring bean),但不在项目 1 中进行特定于平台的修改?
首先,您需要创建自定义 DynamoDemoEntityDeserializer
,如下所示:
class DynamoDemoEntityDeserializer extends JsonDeserializer<DynamoDemoEntity> {
@Override
public DynamoDemoEntity deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
// return DynamoDemoEntity instance;
}
}
然后你可以像下面这样创建 com.fasterxml.jackson.databind.Module
的 bean:
@Bean
public Module dynamoDemoEntityDeserializer() {
SimpleModule module = new SimpleModule();
module.addDeserializer(IDemoEntity.class, new DynamoDemoEntityDeserializer());
return module;
}
Any beans of type com.fasterxml.jackson.databind.Module are automatically registered with the auto-configured Jackson2ObjectMapperBuilder and are applied to any ObjectMapper instances that it creates. This provides a global mechanism for contributing custom modules when you add new features to your application.
如果您想将给定对象反序列化为任何实体,那么您需要为此编写自己的自定义方法。
这是我用过的一个示例,它适用于任何实体。请看看并尝试一下。
@Service
public class ObjectSerializer {
private static final Logger logger = LoggerFactory.getLogger(ObjectSerializer.class);
private static ObjectMapper objectMapper;
@Autowired
private ObjectSerializer(ObjectMapper objectMapper) {
ObjectSerializer.objectMapper = objectMapper;
}
public static <T> T getObject(Object obj, Class<T> class1) {
String jsonObj = "";
T objectDto = null;
try {
jsonObj = objectMapper.writeValueAsString(obj);
objectDto = objectMapper.readValue(jsonObj, class1);
}catch (IOException e) {
logger.error("Exception Occured in ObjectSerializer : | {}",e.getMessage());
}
return objectDto;
}
希望对您有所帮助。