Jackson - 将接口反序列化为枚举类型

7

我有一个接口Event和多个实现该接口的枚举(UserEventBusinessEvent等)。

我想反序列化以下JSON数据:

{
  "event" : "SIGNUP"
}

关于这个bean:

public class Input
{ 
   private Event event;

   public Event getEvent() {..}
   public void setEvent(Event event) {..}
}

public enum UserEvent implements Event
{
    SIGNUP;
}

在这里,我希望将事件反序列化为UserEvent.SIGNUP

我该如何实现这一点?阅读关于@JsonTypeInfo的资料似乎表明需要一个额外的type属性,但在这种情况下,只有一个字符串直接映射到枚举值。


在构建时,所有Event的实现是否都已知,还是它们可以在运行时更改? - rkosegi
如果两个枚举类定义了相同名称的常量会怎样? - Maurice Perry
@rkosegi 他们已知。 - Ali
@MauricePerry 我会确保这不会发生。 - Ali
3个回答

4

您正在使用 Input 中的字段 eventEvent 接口,而 jackson 对于作为该接口实现的 UserEvent 一无所知。

您可以使用自定义的 JsonDeserializer 来获取该值:

public interface Event {
}

public static class Input
{
    private Event event;

    @JsonDeserialize(using = EventDeserializer.class)
    public Event getEvent() {
        return event;
    }

    public void setEvent(Event event) {
        this.event = event;
    }
}

public enum UserEvent implements Event
{
    SIGNUP;
}

public static class EventDeserializer  extends StdDeserializer<Event> {

    protected EventDeserializer() {
        super(Event.class);
    }

    @Override
    public Event deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        return UserEvent.valueOf(p.getText());
    }
}

@Test
public void converEnum() throws Exception {
    ObjectMapper objectMapper = new ObjectMapper();
    Input input = objectMapper.readValue("{\n" +
            "  \"event\" : \"SIGNUP\"\n" +
            "}", Input.class);

    Assert.assertThat(input.getEvent(), Matchers.is(UserEvent.SIGNUP));
}

2
这个解决方案是否会让你陷入只能使用单个具体实现的“Event”字段的境地?由于你在“Event”字段的getter方法上注释了“UserEventDeserializer”,那么如果“Event”字段中的具体值是“BusinessEvent”,会发生什么? - mfunaro
1
@mfunaro 我不这么认为。即使示例仅针对UserEvent,您也可以在“deserilize”方法中编写更复杂的代码,并使用可能的枚举值。 - Vlad Bochenin

2

对于一个单一枚举类型,jsonschema2pojo-maven-plugin 生成的代码如下:

@JsonCreator
public static Foo fromValue(String value) {
    Foo constant = CONSTANTS.get(value);
    if (constant == null) {
        throw new IllegalArgumentException(value);
    } else {
        return constant;
    }
}

我认为您可以编写一个使用@JsonCreator注释的工厂方法,并决定选择哪个枚举。我不确定在哪里放置此方法是否很重要。


我应该在哪里指定这个方法 - 在接口本身上吗? - Ali
就像我之前所说的,我不知道这是否很重要。如果您使用Java SE 8(推荐),那么接口可能是一个选择。 - Puce
我正在使用Java 8。但是在接口上定义它似乎不起作用 - 看起来没有被调用。 - Ali
使用jackson 2.7.6 - Ali

2

我有一个类似的任务,最终创建了一个自定义反序列化器:

public class IFooDeserializer extends StdDeserializer<IFoo> {

    public IFooDeserializer() {
        this(null);
    }

    public IFooDeserializer(Class<?> vc) {
        super(vc);
    }

    @Override
    public IFoo deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        String value = p.getCodec().readValue(p, String.class);
        return Stream.of(GoodFoo.values(), BadFoo.values())
                .flatMap(Stream::of)
                .filter(el -> el.name().equals(value))
                .findAny()
                .orElseThrow(() -> new RuntimeException("Could not deserialize foo: " + value));
    }
}

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