yolgecad
yolgecad

Reputation: 83

Generic Enum Json Deserialization

I needed a better hibernate enum mapping and this page served me well (Except I used char type instead of int).

Next question is how can I serialize/deserialize an enum in a generic way?

Think of a Gender enum:

@JsonSerialize(using = PersistentEnumSerializer.class)
@JsonDeserialize(using = PersistentEnumDeserializer.class)
public enum Gender implements PersistentEnum {

    MALE("M", "Male"), FEMALE("F", "Female");

    private String code;

    private String display;

    Gender(String code, String display) {
        this.code = code;
        this.display = display;
    }

    public String getName() {
        return name();
    }

    public String getCode() {
        return code;
    }

    public String getDisplay() {
        return display;
    }

    public String toString() {
        return display;
    }

}

which implements getName(), getCode() and getDisplay() methods of PersistentEnum interface. Serializing is easy:

public class PersistentEnumSerializer extends JsonSerializer<PersistentEnum> {

    @Override
    public void serialize(PersistentEnum object, JsonGenerator generator, SerializerProvider provider) throws IOException, JsonProcessingException {
        generator.writeStartObject();
        generator.writeFieldName("name");
        generator.writeString(object.getName());
        generator.writeFieldName("code");
        generator.writeString(object.getCode());
        generator.writeFieldName("display");
        generator.writeString(object.getDisplay());
        generator.writeEndObject();
    }
}

but how can I deserialize in java 6? In java 8, I would add a static method to PersistentEnum interface.

public class PersistentEnumDeserializer extends JsonDeserializer<PersistentEnum> {

    @Override
    public PersistentEnum deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        JsonNode node = jp.getCodec().readTree(jp);
        //String value = node.get("name").asText();
        //TODO Somehow I need to get Gender.MALE if the json is {"name":"MALE","code":"M","display":"Male"}
        return null;
    }

}

Upvotes: 3

Views: 2551

Answers (2)

yolgecad
yolgecad

Reputation: 83

While @Justin Jose's solution is not the one I'm looking for (because for each enum we need to add to findEnum method), it gave me a good hint. If getType is implemented like this:

@Override
public String getType() {
    return getClass().getSimpleName();
}

and findEnum like this

private PersistentEnum findEnum(String type, String name) {
    Class<?> c = null;
    try {
        c = Class.forName("enums." + type); //Assuming all PersistentEnum's are in "enums" package
        if (PersistentEnum.class.isAssignableFrom(c)) {
            Method method = c.getMethod("name");
            for (Object object : c.getEnumConstants()) {
                Object enumName = method.invoke(object);
                if (name.equals(enumName))
                    return (PersistentEnum) object;
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

it may work. Not tested and possibly vulnerable.

Upvotes: 1

Justin Jose
Justin Jose

Reputation: 2171

One possible solution is to add a new method getType() to PersistentEnum which will identify the type of Enum.

@JsonSerialize(using = PersistentEnumSerializer.class)
@JsonDeserialize(using = PersistentEnumDeserializer.class)
public enum Gender implements PersistentEnum {
    @Override
    public String getType() {
        return "gender";
    }
}

Serializer should also be modified to include type while serialization.

public class PersistentEnumSerializer extends JsonSerializer<PersistentEnum> {

@Override
public void serialize(PersistentEnum object, JsonGenerator generator, SerializerProvider provider) throws IOException, JsonProcessingException {
    generator.writeStartObject();
    generator.writeFieldName("name");
    generator.writeString(object.getName());
    generator.writeFieldName("code");
    generator.writeString(object.getCode());
    generator.writeFieldName("display");
    generator.writeString(object.getDisplay());
    generator.writeFieldName("type");
    generator.writeString(object.getType());
    generator.writeEndObject();
}
}

Deserializer can be written as shown below.

    public class PersistentEnumDeserializer extends JsonDeserializer<PersistentEnum> {

    @Override
    public PersistentEnum deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        JsonNode node = jp.getCodec().readTree(jp);
        return findEnum(node.get("type").asText(), node.get("name").asText());
    }

    private PersistentEnum findEnum(String type, String name) {
        switch (type) {
        case "gender":
            return Gender.valueOf(name);
        // handle other types here.
        default:
            return null;
        }
    }

}

Upvotes: 1

Related Questions