I looked in many questions related to this but i can't find this exact use case. Assumption here is that i am using Java Jackson library.
I have the following class hierarchy:
public class Event {
@JsonProperty("si")
String sessionId;
@JsonProperty("eventType")
String eventType;
...
}
@JsonSerialize(include= JsonSerialize.Inclusion.NON_NULL)
public class InitEvent extends Event
{
@JsonProperty("pm")
Params params;
public Params getParams()
{
return params;
}
.....
}
@JsonSerialize(include= JsonSerialize.Inclusion.NON_NULL)
public class RecoEvent extends Event
{
@JsonProperty("ti")
String targetId;
@JsonProperty("tt")
int targetType;
public String getTargetId()
{
return targetId;
}
....
}
The rule of deserialization is:
If eventType == 0 then deserialize to a InitEvent
If eventType == 0 then deserialize to a RecoEvent
Out of the box the Jackson deserialization will not work because it does not know which class to deserialize with. One way to handle that is by doing as follows on the base class:
@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="@class")
@JsonSerialize(include= JsonSerialize.Inclusion.NON_NULL)
public class Event
The problem of this solution is assumes the client will serialize with the same mapper since the @class element needs to be present now in the JSON.
My client will not send the extra @class element in the incoming JSON.
What is the required solution?
How I could write a custom deserializer that picks the right derived class based on the eventType value?
Thx in advance
At the end I had to write my own deserializer.
This works in 2 steps, first implement the custom deserializer and then register it.
The implementation works as follows:
public class EvtListDeserializer extends JsonDeserializer<EventList>
{
private static ObjectMapper mapper = new ObjectMapper();
@Override
public EventList deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException, JsonProcessingException
{
EventList eventList = new EventList();
List<Event> listOfEvents = new ArrayList<Event>();
ObjectCodec oc = jsonParser.getCodec();
JsonNode eventListNode = oc.readTree(jsonParser);
ArrayNode node = (ArrayNode) eventListNode.get("evt");
Iterator<JsonNode> events = node.elements();
for (; events.hasNext(); )
{
JsonNode eventNode = events.next();
int eventType = eventNode.get("type").asInt();
Event event;
if (eventType == EventTypes.MoveEvent.getValue())
{
event = mapper.readValue(eventNode.toString(), MoveEvent.class);
}
else if (eventType == EventTypes.CopyEvent.getValue())
{
event = mapper.readValue(eventNode.toString(), CopyEvent.class);
}
else
{
throw new InvalidEventTypeException("Invalid event type:" + eventType);
}
listOfEvents.add(event);
}
eventList.setEvents(listOfEvents);
return eventList;
}
}
Then you just register it as follows in the class that uses your mapper:
public void init()
{
SimpleModule usageModule = new SimpleModule().addDeserializer(EventList.class, new EvtListDeserializer());
mapper.registerModule(usageModule);
}
That works perfectly.
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