I need to use custom deserializer for my json string. The problem is that my class I want to serialize in is very complex and constructed from other classes. But all of the examples in the internet show only very basic way of deserializing jsons (i.e. only retrieving one value by name and getting it's value), but I have subclasses and want to use them so I don't need to write manually all of the mapping. Is there any way I can do this? Example to understand what I'm talking about, so let's say I have this structure:
public TestClass{
public Class1 first;
public Class2 second;
...
public Class10 ten;
}
And all of the classes contain the data, something like this:
public Class1{
public String name;
public int id;
...
}
Obviously I don't want to manually map all of that, but all of the examples in the internet show exactly that, is there any way to read the values into classes directly without needing doing manual mapping? For example most common example is to do something like this:
@Override
public Item deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode node = jp.getCodec().readTree(jp);
int id = (Integer) ((IntNode) node.get("id")).numberValue();
String itemName = node.get("itemName").asText();
int userId = (Integer) ((IntNode) node.get("createdBy")).numberValue();
return new Item(id, itemName, new User(userId, null));
}
So my question is can I do this more easily, i.e. reading values directly into these classes? (Except the one, that's why I need the custom deserilizing at the first place, but it's better to write only one manual mapping instead of 10). Summurizing, I want to do something like this:
@Override
public Item deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
...
Testclass test = new Testclass();
Class1 class1 = json...parse(..., Class1.class);
Class2 class2 = json...parse(..., Class2.class);
...
test.setClass1(class1);
test.setClass2(class2);
...
Class10 manualClass = new Class10();
manualClass.setField1(json.get("class10").get("field1").stringValue());
...
test.setClass10(manualClass);
}
The answer by Oleg is a good one for what you want to do.
There is a little known feature with Jackson called Mixins. They allow you to provide overrides, annotations and any other desired modifications just to specific places in your complex object graph. This means that you can customize specific aspects of the serialization without having to implement a whole new serializer.
ObjectMapper mapper = new ObjectMapper();
mapper.addMixInAnnotations(Class1.class, Class1Mixin.class);
public Class1
{
public String name;
public int id;
}
public abstract Class1Mixin
{
// Override 'name' to serialize as 'fullName'. Leave the id the same.
@JsonProperty("fullName")
public String name;
}
Think of if as being able to overlay annotations on top of little sections of the object graph without polluting your original classes. Super handy for keeping Models clean and using Mixins to specify how to make them Data Transfer Objects(DTO)/Messages.