Jackson's special deserialization for polymorphic objects

I looked through many questions related to this, but I cannot find this specific use case. Suppose I use the 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; } .... } 

Deserialization rule:

  • If eventType == 0, then deserialize in InitEvent

  • If eventType == 0, then deserialize in RecoEvent

From the discharge, Jackson's deserialization will not work, because he does not know for which class to deserialize. One way to handle this is as follows: base class:

 @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="@class") @JsonSerialize(include= JsonSerialize.Inclusion.NON_NULL) public class Event 

The problem with this solution assumes that the client will serialize with the same mapper since the @class element must be present in JSON.

My client will not send an additional @class element in incoming JSON.

What is the required solution?

How can I write my own deserializer that selects the correct derived class based on the value of eventType?

thanks in advance

+5
source share
1 answer

In the end, I had to write my own deserializer.

This works in 2 steps, first implements a custom deserializer, and then registers 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 simply register it as follows in the class that your cartographer uses:

 public void init() { SimpleModule usageModule = new SimpleModule().addDeserializer(EventList.class, new EvtListDeserializer()); mapper.registerModule(usageModule); } 

This works great.

+2
source

Source: https://habr.com/ru/post/1206983/


All Articles