反序列化具有动态字段名称的嵌套 JSON 对象

Deserialise nested JSON object with dynamic field name

我正在使用 Spring RestTemplate 和 Jackson 2.1,我正在尝试反序列化下面的 JSON 字符串。而第一个数字是唯一 ID,因此这将是动态的:

{"2127388":{"name":"John","city":"Amsterdam","country":"The Netherlands"}}

我使用 RestTemplate 执行以下操作:

final ResponseEntity<UserDetailsWrapper> re = restTemplate.getForEntity(apiUrl, UserDetailsWrapper.class);

我拥有的 POJO 是

class UserDetailsWrapper {
    private long uniqueId; // [getter + setter]
    private UserDetails userDetails; // [getter + setter]

    // no args constructor + all properties constructor
}

class UserDetails {
    private String name; // [getter + setter]
    private String city; // [getter + setter]
    private String country; // [getter + setter]

    // no args constructor + all properties constructor
}

UserDetailsWrapper class 已实例化,但其所有属性仍为空。

当我简单地做:

{"name":"John","city":"Amsterdam","country":"The Netherlands"}

我能够反序列化到 UserDetails class 并按预期填充所有属性,因此我的配置应该是有序的。可能我需要在特定位置注释 UserDetailsWrapper class 或者我需要一个自定义反序列化器。我都试过了,但老实说,我不知道该怎么做。

如果有人能帮我解决这个问题,我会再次成为一个快乐的人。

反映您的 POJO 结构的有效负载为:

{"uniqueId":"2127388", "userDetails":{"name":"John","city":"Amsterdam","country":"The Netherlands"}}

我认为使用变量 JSON 属性 名称(在您的情况下为 2127388)不是一个好主意。只需更改有效负载结构并按原样使用您的 POJO。

对评论的反应:

我个人会重新考虑使用这样的 API,因为它显然很糟糕 API。根据JSON spec,属性的名称应该是字符串,而这里显然是语义上的数字。

如果你被迫使用它,只需使用 Map<String, UserDetail> 作为你的包装器。它显然只有一个元素。

从 class 名称来看,您真的不想要 Wrapper,您想要 Map,类似于此答案中的内容:

而不是他们在那里使用的String,使用Long(未经测试):

TypeFactory typeFactory = mapper.getTypeFactory();
MapType mapType = typeFactory.constructMapType(HashMap.class, Long.class, UserDetails.class);
HashMap<Long, UserDetails> map = mapper.readValue(json, mapType);

通过使用您提供的类型调用 TypeFactory.constructType 来深入研究代码,这正是 RestTemplate 在内部所做的,不幸的是在途中丢失了您的泛型:

restTemplate.getForEntity("apiUrl", Map.class);

not alone 注意到缺少泛型支持,链接的答案指向您已经找到的内容:

restTemplate.exchange("apiUrl", 
        HttpMethod.GET,
        null,
        new ParameterizedTypeReference<Map<Long, UserDetails>>(){});

我基本上是用一个HashMap解决了,然后得到map的第一个entry

final ParameterizedTypeReference<Map<Long, UserDetails>> ptr =
            new ParameterizedTypeReference<Map<Long, UserDetails>>(){};

final ResponseEntity<Map<Long, UserDetails>> re =
    restTemplate.exchange("apiUrl", HttpMethod.GET, null, ptr);

因为 RestTemplate::getForEntity() 不支持添加类型引用我不得不使用 RestTemplate::exchange()