【问题标题】:How to deserialize JSON to interface?如何将 JSON 反序列化为接口?
【发布时间】:2018-01-03 14:43:02
【问题描述】:

我无法将 JSON 反序列化为以下示例中实现 Basic 接口的某些类 ChildAChildB 等。 p>

@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.PROPERTY,
        property = "type")
@JsonSubTypes({
        @JsonSubTypes.Type(value = InstagramUser.class, name = "ChildA")
})
public interface Basic {
    getName();
    getCount();
}

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeName("ChildA")
public class ChildA implements Basic { ... }

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeName("ChildB")
public class ChildB implements Basic { ... }
...

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class Response<E extends Basic> {
    @JsonProperty("data")
    private List<E> data;

    public List<E> getData() {
        return data;
    }

    public void setData(List<E> data) {
        this.data = data;
    }
}

// deserialization
HTTPClient.objectMapper.readValue(
    response, 
    (Class<Response<ChildA>>)(Class<?>) Response.class
)

例外情况是:com.fasterxml.jackson.databind.JsonMappingException:意外令牌 (END_OBJECT),预期 FIELD_NAME:缺少包含类型 ID 的属性“类型”(对于 Basic 类)

预期的 JSON 是这样的:

{
    "data": [{ ... }, ...]
}

所有类型对象中都没有属性,因此它们完全不同。但正如您在 readValue 行上看到的那样,我知道预期的类型。如何构造 JsonTypeInfoJsonSubTypes 注释以将 JSON 反序列化为预期的类?

【问题讨论】:

  • 是json中要反序列化的类型吗?
  • “数据”中的对象是预期格式(ChildA),但没有包含信息的属性是什么类型。列表中的所有对象都是相同的。不幸的是,我无法更改 JSON。
  • 这就是问题所在 - 您的注释告诉杰克逊期望 json 中有一个类型,但没有一个

标签: java json jackson polymorphism deserialization


【解决方案1】:

我有点和你一样的问题,根据这里的阅读:Jackson Deserialize Abstract Classes我创建了自己的解决方案,它基本上包括创建我自己的反序列化器,诀窍是使用/识别 JSON 中的特定属性以了解反序列化应该返回哪个实例类型,例如:

public interface Basic {
}

第一个孩子:

public class ChildA implements Basic {
    private String propertyUniqueForThisClass;
    //constructor, getters and setters ommited
}

二孩:

public class ChildB implements Basic {
    private String childBUniqueProperty;
    //constructor, getters and setters ommited
}

反序列化器 (BasicDeserializer.java) 如下:

public class BasicDeserializer extends StdDeserializer<Basic> {


    public BasicDeserializer() {
        this(null);
    }

    public BasicDeserializer(final Class<?> vc) {
        super(vc);
    }

    @Override
    public Basic deserialize(final JsonParser jsonParser,
                               final DeserializationContext deserializationContext)
            throws IOException {

        final JsonNode node = jsonParser.getCodec().readTree(jsonParser);
        final ObjectMapper mapper = (ObjectMapper) jsonParser.getCodec();

        // look for propertyUniqueForThisClass property to ensure the message is of type ChildA
        if (node.has("propertyUniqueForThisClass")) {
            return mapper.treeToValue(node, ChildA.class);
            // look for childBUniqueProperty property to ensure the message is of type ChildB
        } else if (node.has("childBUniqueProperty")) {
            return mapper.treeToValue(node, ChildB.class);
        } else {
            throw new UnsupportedOperationException(
                    "Not supported class type for Message implementation");
        }
    }
}

最后,您将拥有一个实用程序类 (BasicUtils.java):

private static final ObjectMapper MAPPER;

// following good software practices, utils can not have constructors
private BasicUtils() {}

static {
    final SimpleModule module = new SimpleModule();
    MAPPER = new ObjectMapper();
    module.addDeserializer(Basic.class, new BasicDeserializer());
    MAPPER.registerModule(module);
}

public static String buildJSONFromMessage(final Basic message)
        throws JsonProcessingException {
    return MAPPER.writeValueAsString(message);
}

public static Basic buildMessageFromJSON(final String jsonMessage)
        throws IOException {
    return MAPPER.readValue(jsonMessage, Basic.class);
}

用于测试:

@Test
public void testJsonToChildA() throws IOException {
    String message = "{\"propertyUniqueForThisClass\": \"ChildAValue\"}";
    Basic basic = BasicUtils.buildMessageFromJSON(message);
    assertNotNull(basic);
    assertTrue(basic instanceof ChildA);
    System.out.println(basic);
}
@Test
public void testJsonToChildB() throws IOException {
    String message = "{\"childBUniqueProperty\": \"ChildBValue\"}";
    Basic basic = BasicUtils.buildMessageFromJSON(message);
    assertNotNull(basic);
    assertTrue(basic instanceof ChildB);
    System.out.println(basic);
}

源代码可以在:https://github.com/darkstar-mx/jsondeserializer

【讨论】:

    【解决方案2】:

    我发现不完全是解决方案,而是一种解决方法。我使用了自定义响应类 ChildAResponse 并将其传递给 ObjectMapper.readValue() 方法。

    class ChildAResponse extends Response<ChildA> {}
    
    // deserialization
    HTTPClient.objectMapper.readValue(
        response, 
        ChildAResponse.class
    )
    

    所以不再需要接口上的JsonTypeInfo和JsonSubTypes注解了。

    【讨论】:

      猜你喜欢
      • 2019-06-11
      • 1970-01-01
      • 2016-11-09
      • 1970-01-01
      • 2017-02-28
      • 2011-05-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多