反序列化具有动态字段名称的嵌套 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()
我正在使用 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()