Reputation: 1269
When I deserialize an object, I want to do some transformation on the json (move/change/add fields) then continue processing the deserialized object. Is this possible?
Simple example:
Input JSON
{
"first": "thing",
"seconds": [ 55, 67, 12 ]
}
My Object
public class MyObject {
private String new;
private int second;
// getters and setters
}
Deserializer
public class MyObjectDeserializer extends JsonDeserializer<MyObject> {
@Override
public MyObject deserialize(final JsonParser p, final DeserializationContext ctxt) throws IOException {
JsonNode json = p.getCodec().readTree(p);
JsonNode translatedJson = translate(json);
// continue processing MyObject like ObjectMapper#readValue would using the translated json
}
private JsonNode translate(final JsonNode json) {
ObjectNode object = (ObjectNode) json;
// Update 'first' to 'new'
object.put("new", object.get("first").asText()).remove("first");
// Find the max in 'seconds' and add it as 'second'
JsonNode seconds = object.get("seconds");
int max = 0;
for (int i = 0; i < seconds.size(); i++) {
max = Math.max(max, seconds.get(i).asInt());
}
object.put("second", max).remove("seconds");
return object;
}
}
Upvotes: 1
Views: 1888
Reputation: 17066
I would prefer to annotate MyObject
rather than implement a separate deserializer. Here is an example that allows you to round-trip the data from JSON to Java and back again.
public static void main(String... args) throws Exception {
String json = "{\"first\": \"thing\", \"seconds\": [55, 67, 12]}";
ObjectMapper mapper = new ObjectMapper();
MyObject mo = mapper.readValue(json, MyObject.class);
System.out.println(mo);
}
public static class MyObject {
private final String news;
private final List<Integer> seconds;
private final int max;
@JsonCreator
public MyObject(@JsonProperty("first") String news,
@JsonProperty("seconds") List<Integer> seconds) {
this.news = news;
this.seconds = seconds;
this.max = Collections.max(seconds);
}
public String getNew() {
return news;
}
public int getSecond() {
return max;
}
@Override
public String toString() {
return "{\"first\": \"" + news + "\", \"seconds\": " + seconds + "}";
}
}
Upvotes: 2
Reputation: 15086
Yes, you can do this using the ObjectCodec
from the JsonParser
:
public class MyObjectDeserializer extends JsonDeserializer<MyObject> {
@Override
public MyObject deserialize(final JsonParser p, final DeserializationContext ctxt) throws IOException {
ObjectCodec codec = p.getCodec();
JsonNode json = coded.readTree(p);
JsonNode translatedJson = translate(json);
// continue processing MyObject like ObjectMapper#readValue would using the translated json
return codec.treeToValue(node, MyObject.class);
}
private JsonNode translate(final JsonNode json) {
ObjectNode object = (ObjectNode) json;
// Update 'first' to 'new'
object.put("new", object.get("first").asText()).remove("first");
// Find the max in 'seconds' and add it as 'second'
JsonNode seconds = object.get("seconds");
int max = 0;
for (int i = 0; i < seconds.size(); i++) {
max = Math.max(max, seconds.get(i).asInt());
}
object.put("second", max).remove("seconds");
return object;
}
}
Note that you won't be able to use new
as field name in the Java class because it is a reserved keyword.
Upvotes: 1