【问题标题】:How to provide a custom deserializer with Jackson and Spring Boot如何使用 Jackson 和 Spring Boot 提供自定义反序列化器
【发布时间】:2018-11-26 11:51:22
【问题描述】:

我有以下三个应用程序:

项目 1 举行

  • 业务逻辑(Spring Cloud Function)
  • 一个接口IDemoEntity

项目 2

  • AWS 特定的处理程序
  • IDemoEntity 的一种实现,带有 DynamoDB 特定的注释
  • 项目基于Spring Boot

项目 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. 一次性实现业务逻辑和数据模型(在项目 1 中)(利用 Spring Cloud Function)
  2. 只为每个平台实现一个包装器项目(我从项目 2 中的 AWS Lambda 开始,但 Azure 的项目 3 看起来很相似),以及特定于平台的东西(如实体实现,它需要特定于 DB注释)
  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 中进行特定于平台的修改?

【问题讨论】:

    标签: java spring-boot jackson-databind


    【解决方案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;
    }
    

    com.fasterxml.jackson.databind.Module 类型的任何 bean 都会自动注册到自动配置的 Jackson2ObjectMapperBuilder 并应用于它创建的任何 ObjectMapper 实例。当您向应用程序添加新功能时,这提供了一种用于贡献自定义模块的全局机制。

    来源:howto-customize-the-jackson-objectmapper

    【讨论】:

    • 太棒了,你的第二段代码正是我要找的!非常感谢!
    【解决方案2】:

    如果您想将给定对象反序列化为任何实体,那么您需要为此编写自己的自定义方法。

    这是一个我用过的例子,它适用于任何实体。请查看并尝试。

    @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;
        }
    

    我希望这会有所帮助。

    【讨论】:

    • 非常感谢您的回答!然而,下面 Sukhpal Singh 的答案是缺失的部分:如何注册自定义反序列化器。
    猜你喜欢
    • 2017-08-10
    • 2021-11-07
    • 1970-01-01
    • 1970-01-01
    • 2021-03-31
    • 2020-09-22
    • 2021-12-20
    • 2019-08-10
    • 2016-12-31
    相关资源
    最近更新 更多