【问题标题】:Deserialization with @JsonSubTypes for no value - missing property error使用 @JsonSubTypes 反序列化没有值 - 缺少属性错误
【发布时间】:2015-01-22 13:02:56
【问题描述】:

我像这样反序列化 json:

{
  "type":"a",
  "payload" : {...}
}

有效载荷类型取决于类型。我的班级:

public class Sth<T extends Payload> {

    @JsonProperty("type")
    private String type;
    @Valid
    private T payload;

    @JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
        property = "type",
        visible = true,
        defaultImpl = NoClass.class)
    @JsonSubTypes({
        @JsonSubTypes.Type(value = APayload.class, name = "a"),
        @JsonSubTypes.Type(value = BPayload.class, name = "b"),
        @JsonSubTypes.Type(value = CPayload.class, name = "c")})
    public void setPayload(T payload) {
    this.payload = payload;
    }

    public void setType(String type) {
    this.type = type;
    }

}

我还输入了没有有效负载的“d”。如果我尝试反序列化:

{
  "type":"d",
  "payload" : null
}

它可以工作,但在没有有效负载的情况下无法工作:

{
  "type":"d",
}

如何使它与上一个示例一起工作?

我得到的错误堆栈跟踪:

[error] Caused by: com.fasterxml.jackson.databind.JsonMappingException: Missing property 'payload' for external type id 'type
[error]  at [Source: N/A; line: -1, column: -1]
[error]     at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:164)
[error]     at com.fasterxml.jackson.databind.DeserializationContext.mappingException(DeserializationContext.java:700)
[error]     at com.fasterxml.jackson.databind.deser.impl.ExternalTypeHandler.complete(ExternalTypeHandler.java:160)
[error]     at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeWithExternalTypeId(BeanDeserializer.java:690)
[error]     at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeWithExternalTypeId(BeanDeserializer.java:639)
[error]     at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:266)
[error]     at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:124)
[error]     at com.fasterxml.jackson.databind.ObjectMapper._readValue(ObjectMapper.java:2965)
[error]     at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:1587)
[error]     at com.fasterxml.jackson.databind.ObjectMapper.treeToValue(ObjectMapper.java:1931)
[error]     at play.libs.Json.fromJson(Json.java:47)

【问题讨论】:

    标签: java json jackson


    【解决方案1】:

    对此有明确的反序列化功能:

    new ObjectMapper().configure(
    DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY, 
    false);
    

    【讨论】:

    • 更改配置是很好的解决方案,但最好添加到应用程序属性中:spring.jackson.deserialization.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY
    • 那是 Spring Boot 特有的。如果您不使用 Spring Boot,您仍然需要配置对象映射器。
    【解决方案2】:

    我也遇到过这个问题,使用 Jackson 提供的机制(自定义 BeanDeserializerBeanDeserializerModifier 等)找不到优雅的解决方案。

    这看起来像是处理外部类型 ID 的方式中的一个错误。我通过以下方式解决了它:

    1. 将 JSON 字符串反序列化为 JsonNode
    2. 如果所需属性不存在,则手动插入null 节点;
    3. JsonNode 映射到我想要的值类型。

    我的代码如下所示:

    public <T> T decode(String json, Class<T> type) throws IOException {
        JsonNode jsonNode = mapper.readTree(json);
    
        if (jsonNode.isObject() && (jsonNode.get("payload") == null  || jsonNode.get("payload").size() == 0)) {
            ObjectNode objectNode = (ObjectNode) jsonNode;
            objectNode.putNull("payload");
        }
    
        return mapper.treeToValue(jsonNode, type);
    }
    

    【讨论】:

    • 这实际上是我所做的。我在杰克逊论坛和 AFAIK 上问了同样的问题,没有比这更好的解决方案了。你错过了一个案例。当有效负载未设置或设置为 {} 时,Jackson 无法正确反序列化对象,因此我编辑了您的答案并接受了它。
    • @jasiustasiu 你的编辑被拒绝了吗?你想改变什么?
    • 我不知道为什么我的编辑被拒绝了。我再次编辑了您的答案,现在似乎可以使用了
    • 是的,我自己批准了。当您第一次进行编辑时,我不在,因此被其他审阅者拒绝,可能是因为他们认为更改太大。
    【解决方案3】:

    我相信我遇到了与您记录的相同的问题。

    我收到以下异常

    com.fasterxml.jackson.databind.JsonMappingException: Missing property 'content' 
      for external type id 'contentType
    

    我最终通过将 @JsonTypeId 注释添加到类型字段来使其工作。

    public interface Content {
    }
    
    public class Post implements Content {
      public String content = "bar";
    }
    
    public class Foo {
    
      @JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
                    include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
                    property = "contentType")
      @JsonSubTypes({
        @JsonSubTypes.Type(value = Post.class, name = "post")
      })
      public Content content;
    
      @JsonTypeId
      public ContentType contentType;
    
      public enum ContentType {
        "post"
      }
    }
    

    【讨论】:

      猜你喜欢
      • 2020-06-22
      • 1970-01-01
      • 2021-06-11
      • 1970-01-01
      • 2021-12-13
      • 1970-01-01
      • 2015-03-17
      • 1970-01-01
      • 2021-12-12
      相关资源
      最近更新 更多