如何使用Jackson和Spring Boot提供自定义解串器

时间:2018-11-26 11:51:22

标签: spring-boot jackson-databind

我有以下三个应用程序:

  

项目1持有

  • 业务逻辑(Spring Cloud功能)
  • 接口 IDemoEntity
  

项目2

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

项目3

  • IDemoEntity 的一个实现,带有CosmosDB批注
  • 天蓝色特定的处理程序

项目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 时,杰克逊抛出以下异常:

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

这是有道理的,因为杰克逊不知道如果IDemoEntity将对接收到的JSON反序列化到哪个实现。

现在最简单的方法是在@JsonDeserialize(as = DynamoDemoEntity.class)上放置一个IDemoEntity

但是,这将破坏我的完整结构:项目1将不包含任何信息,即编译哪个平台特定项目的信息。

有什么想法,我如何提供自定义解串器(例如Spring bean),而又不对项目1进行平台特定的修改?

2 个答案:

答案 0 :(得分:4)

首先,您需要创建自定义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

答案 1 :(得分:0)

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

这是我使用过的示例,它适用于任何实体。请看看并尝试。

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

我希望这会有所帮助。