杰克逊自定义反序列化多态对象

5

我将翻译与此相关的许多问题,但我无法找到这个确切的用例。

这里的假设是我正在使用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 == 1,则反序列化为RecoEvent

由于Jackson反序列化不知道要使用哪个类进行反序列化,因此默认情况下无法正常工作。解决方法之一是在基类上执行以下操作:

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

这个解决方案的问题在于它假定客户端将使用相同的mapper进行序列化,因为现在JSON中需要存在@class元素。
但是我的客户端不会在传入JSON中发送额外的@class元素。
需要什么解决方案?
我如何编写自定义反序列化程序来基于eventType值选择正确的派生类?
提前感谢。

我在一个开源项目中找到了这个示例。属性"op"被配置为用作子类型实例化的类型提示。也许这有所帮助?最好的问候,Marius。 - Marius Schmidt
1个回答

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

那很完美。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接