杰克逊为多态对象定制反序列化

时间:2014-11-16 15:51:12

标签: java json serialization jackson

我查看了许多与此相关的问题,但我无法找到这个确切的用例。 这里假设我正在使用Java Jackson库。

我有以下类层次结构:

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;
     }

    ....
}

反序列化的规则是:

  • 如果eventType == 0则反序列化为InitEvent

  • 如果eventType == 0则反序列化为RecoEvent

开箱即用的杰克逊反序列化将不起作用,因为它不知道要反序列化的类。处理它的一种方法是在基类上执行以下操作:

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

此解决方案的问题是假设客户端将使用相同的映射器进行序列化,因为@class元素现在需要存在于JSON中。

我的客户端不会在传入的JSON中发送额外的@class元素。

所需的解决方案是什么?

我如何编写一个自定义反序列化器,根据eventType值选择正确的派生类?

提前谢谢

1 个答案:

答案 0 :(得分:2)

最后我必须编写自己的反序列化器。

这有两个步骤,首先实现自定义反序列化器,然后注册它。

实施如下:

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;
    }
}

然后,您只需在使用映射器的类中注册如下:

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

完美无缺。