Jackson Json 将对象反序列化为列表
Jackson Json deserialization of an object to a list
我正在使用 Spring 的 RestTemplate
并使用 Jackson
进行反序列化。
在我的 JSON 服务器响应中,其中一个字段可以是对象或列表。这意味着它可以是 "result": [{}]
或 "result": {}
.
有没有办法通过对我要反序列化的类型的注释来处理这类事情?将成员定义为 array[]
或 List<>
并在第二个示例中插入单个对象?
我可以写一个新的 HttpMessageConverter
来处理它吗?
既然你用的是 Jackson,我想你需要的是 JsonDeserializer
class (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 字段。假设您有 class 个 Result
和 ResultItem
:
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);
result1
和 result2
包含相同的值。
您可以使用 Jackson 中的配置标志实现您想要的 ObjectMapper
:
ObjectMapper mapper = Jackson2ObjectMapperBuilder.json()
.featuresToEnable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
.build();
只需按照 this answer 中的说明将此 ObjectMapper
实例设置为您的 RestTemplate
,并且在您要反序列化的 class 中,始终使用集合,即List
:
public class Response {
private List<Result> result;
// getter and setter
}
我正在使用 Spring 的 RestTemplate
并使用 Jackson
进行反序列化。
在我的 JSON 服务器响应中,其中一个字段可以是对象或列表。这意味着它可以是 "result": [{}]
或 "result": {}
.
有没有办法通过对我要反序列化的类型的注释来处理这类事情?将成员定义为 array[]
或 List<>
并在第二个示例中插入单个对象?
我可以写一个新的 HttpMessageConverter
来处理它吗?
既然你用的是 Jackson,我想你需要的是 JsonDeserializer
class (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 字段。假设您有 class 个 Result
和 ResultItem
:
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);
result1
和 result2
包含相同的值。
您可以使用 Jackson 中的配置标志实现您想要的 ObjectMapper
:
ObjectMapper mapper = Jackson2ObjectMapperBuilder.json()
.featuresToEnable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
.build();
只需按照 this answer 中的说明将此 ObjectMapper
实例设置为您的 RestTemplate
,并且在您要反序列化的 class 中,始终使用集合,即List
:
public class Response {
private List<Result> result;
// getter and setter
}