如何基于共同领域杰克逊抽象地反序列化不同的枚举类型?

时间:2015-04-18 17:29:14

标签: java json generics enums jackson

问题:

我将杰克逊的enum反序列化与代码中的名称不符,下面是json的示例。

{
    "thing1": {"foo": "cool-guy"},
    "thing2": {"foo": "loser-face"}
}

以下是enum,我稍后会解释interface

enum Foo implements HasText {
    COOL_GUY("cool-guy"), LOSER_FACE("loser-face"), // etc...

    private String text;

    private Foo(String text) {
        this.text = text;
    }

    @Override
    public String getText() {
        return text;
    }
}

我知道如何通过在foo的setter方法上创建反序列化器(下面)和注释enum来为每个@JsonDeserialize(using = FooDeserializer .class) 单独解决此问题。

public class FooDeserializer extends JsonDeserializer<Enum<Foo>> {
    @Override
    public Foo deserialize(JsonParser p, DeserializationContext context) throws Exception {

      if (p.getCurrentToken().equals(JsonToken.VALUE_STRING)) {
        String jsonText = p.getText();
        Stream<Foo> stream = Arrays.asList(Foo.values()).stream();
        return stream.filter(a -> a.getText().equals(jsonText.toLowerCase())).findAny().get();
      }

      throw context.mappingException(Foo.class);
    }
}

问题:

有没有办法抽象地这样做?这就是为什么我将HasText interface添加到我的所有枚举中,希望有办法做这样的事情:

public class EnumWithTextDeserializer<T extends Enum<T> & HasText> extends JsonDeserializer<T> {

    @Override
    public T deserialize(JsonParser p, DeserializationContext context) throws Exception {
        if (p.getCurrentToken().equals(JsonToken.VALUE_STRING)) {
          final String jsonText = p.getText();
          final Stream<T> stream = Arrays.asList(runtimeClass().getEnumConstants()).stream();
          return stream.filter(a -> a.getText().equals(jsonText.toLowerCase())).findAny().get();
        }
        throw context.mappingException(runtimeClass());
    }

    @SuppressWarnings("unchecked")
    private Class<T> runtimeClass() {
        ParameterizedType superclass = (ParameterizedType) getClass().getGenericSuperclass();
        return (Class<T>) superclass.getActualTypeArguments()[0];
    }
}

编译不会让我用这个类注释setter方法(@JsonDeserialize(using = EnumWithTextDeserializer.class)),因为

Type mismatch: cannot convert from Class<EnumWithTextDeserializer> to Class<? extends JsonDeserializer<?>>".

真的,我希望能够做的就是根据getText()方法对这些enum进行反序列化。

1 个答案:

答案 0 :(得分:1)

为了反序列化,您可以使用@JsonValue指定字符串值。

public enum FooEnum implements WithText {
    AWESOME("awesome-rad"),
    NARLY("totally-narly");

    private final String text;

    FooEnum(String text) {
        this.text = text;
    }

    @Override
    @JsonValue
    public String getText() {
        return text;
    }

}

然后执行此代码以序列化/反序列化

    ImmutableMap<String, FooEnum> map = ImmutableMap.of("value", FooEnum.AWESOME, "value2", FooEnum.NARLY);
    final String value;
    try {
        value = objectMapper.writeValueAsString(map);
    } catch (JsonProcessingException e) {
        throw Throwables.propagate(e);
    }

    Map<String, FooEnum> read;
    try {
        read = objectMapper.readValue(value, new TypeReference<Map<String, FooEnum>>() {});
    } catch (IOException e) {
        throw Throwables.propagate(e);
    }

我明白了:

read = {LinkedHashMap@4627}  size = 2
  0 = {LinkedHashMap$Entry@4631} "value1" -> "AWESEOME"
  1 = {LinkedHashMap$Entry@4632} "value2" -> "NARLY"