I'm consuming a web service using Spring's RestTemplate
and deserializing with Jackson
.
In my JSON response from the server, one of the fields can be either an object or a list. meaning it can be either "result": [{}]
or "result": {}
.
Is there a way to handle this kind of things by annotations on the type I'm deserializing to ? define the member as an array[]
or List<>
and insert a single object in case of the second example ?
Can I write a new HttpMessageConverter
that will handle it ?
Since you are using Jackson I think what you need is JsonDeserializer
class (javadoc).
You can implement it like this:
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> {}
Next you need to annotate your POJO field. Let's say you have classes like Result
and 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;
}
}
Now you can check your deserializer:
// 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
and result2
contain the same value.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With