Ali
Ali

Reputation: 267049

Jackson - Deserialize Interface to enum

I have an interface Event and multiple enums that implement this interface (UserEvent, BusinessEvent, etc).

I want to deserialize the following json data:

{
  "event" : "SIGNUP"
}

To this bean:

public class Input
{ 
   private Event event;

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

public enum UserEvent implements Event
{
    SIGNUP;
}

Here, i'd like event to be deserialized to UserEvent.SIGNUP.

How can I accomplish this? Reading up on @JsonTypeInfo seems to indicate that an additional type attribute would be needed, but in this case, there's just one string which maps directly to an enum value.

Upvotes: 7

Views: 2642

Answers (3)

Vlad Bochenin
Vlad Bochenin

Reputation: 3072

You are using Event interface for field event in Input and jackson doesn't know anything about UserEvent as implementation of this interface.

You can use custom JsonDeserializer to get value:

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

Upvotes: 4

Zaziro
Zaziro

Reputation: 543

Having a similar task I ended up creating a custom deserializer:

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

Upvotes: 2

Puce
Puce

Reputation: 38122

For a single enum the jsonschema2pojo-maven-plugin generates code like the following:

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

I guess you could write a factory method which is annotated with @JsonCreator and somehow decides which enum to choose. I'm not sure if it matters much where you put this method.

Upvotes: 2

Related Questions