【问题标题】:Jackson Json deserialization of an object to a listJackson Json 将对象反序列化为列表
【发布时间】:2023-03-13 12:30:01
【问题描述】:

我正在使用 Spring 的 RestTemplate 使用 Web 服务并使用 Jackson 进行反序列化。

在来自服务器的 JSON 响应中,其中一个字段可以是对象或列表。这意味着它可以是"result": [{}]"result": {}

有没有办法通过对我要反序列化的类型进行注释来处理这种事情?将成员定义为array[]List<> 并在第二个示例的情况下插入单个对象? 我可以写一个新的HttpMessageConverter 来处理它吗?

【问题讨论】:

  • 尝试发送map而不是List,如果你设置了Hashmap,你总是会收到响应{}。
  • @Mitsu 不确定你的意思。我只实现客户端。

标签: java json serialization jackson resttemplate


【解决方案1】:

由于您使用的是 Jackson,我认为您需要的是 JsonDeserializer 类 (javadoc)。

你可以这样实现:

public class ListOrObjectGenericJsonDeserializer<T> extends JsonDeserializer<List<T>> {

    private final Class<T> cls;

    public ListOrObjectGenericJsonDeserializer() {
        final ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass();
        this.cls = (Class<T>) type.getActualTypeArguments()[0];
    }

    @Override
    public List<T> deserialize(final JsonParser p, final DeserializationContext ctxt) throws IOException, JsonProcessingException {
        final ObjectCodec objectCodec = p.getCodec();
        final JsonNode listOrObjectNode = objectCodec.readTree(p);
        final List<T> result = new ArrayList<T>();
        if (listOrObjectNode.isArray()) {
            for (JsonNode node : listOrObjectNode) {
                result.add(objectCodec.treeToValue(node, cls));
            }
        } else {
            result.add(objectCodec.treeToValue(listOrObjectNode, cls));
        }
        return result;
    }
}

...

public class ListOrObjectResultItemJsonDeserializer extends ListOrObjectGenericJsonDeserializer<ResultItem> {}

接下来您需要注释您的 POJO 字段。假设你有 ResultResultItem 这样的类:

public class Result {

    // here you add your custom deserializer so jackson will be able to use it
    @JsonDeserialize(using = ListOrObjectResultItemJsonDeserializer.class)
    private List<ResultItem> result;

    public void setResult(final List<ResultItem> result) {
    this.result = result;
    }

    public List<ResultItem> getResult() {
        return result;
    }
}

...

public class ResultItem {

    private String value;

    public String getValue() {
        return value;
    }

    public void setValue(final String value) {
        this.value = value;
    }
}

现在你可以检查你的反序列化器了:

// list of values
final String json1 = "{\"result\": [{\"value\": \"test\"}]}";
final Result result1 = new ObjectMapper().readValue(json1, Result.class);
// one value
final String json2 = "{\"result\": {\"value\": \"test\"}}";
final Result result2 = new ObjectMapper().readValue(json2, Result.class); 

result1result2 包含相同的值。

【讨论】:

  • 酷。可以通用吗?说类似public List&lt;T&gt; deserialize(final JsonParser p, final DeserializationContext ctxt) throws IOException, JsonProcessingException {
  • 我把它改成了通用的。但是你必须扩展通用基类并指定字段类型。
  • result.add(objectCodec.treeToValue(node, cls)); // 这是试图解析列表的项目,也作为列表。这是正确的吗?
【解决方案2】:

您可以通过 Jackson 的 ObjectMapper 中的配置标志来实现您想要的:

ObjectMapper mapper = Jackson2ObjectMapperBuilder.json()
    .featuresToEnable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
    .build();

只需按照this answer 中的说明将此ObjectMapper 实例设置为您的RestTemplate,并在您要反序列化的类中,始终使用集合,即List

public class Response {

    private List<Result> result;

    // getter and setter
}

【讨论】:

  • 听起来不错,我可以在特定字段上设置这个标志吗?我不想将它应用于每个对象。
  • @Nati 我不相信您可以将其应用于特定领域。但是,如果您的 JSON 中有一个字段并且您确定它永远不会是一个集合,那么您可以在您的 java Response 类中有一个字段属性(即不是 List)。跨度>
  • 成功了!你唯一需要改变的是ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().build();而不是new对象,然后我会接受答案
  • @Nati 完成。还改变了设置标志的方式。
猜你喜欢
  • 1970-01-01
  • 2018-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-12
  • 2016-05-30
相关资源
最近更新 更多