Jackson deserialize Enums with multiple names

I have problems deserializing Enumerations that have multiple names for a value. Here's an example: Info is a Java class that has an enumeration inside with several names:

public class Info {
    //...
    private ContainerFormat format;
}

// ContainerFormat.java:

public enum ContainerFormat {
    //  ....
    MP4("mp4", "mpeg4"),
    UNKNOWN("null");

    private String name;
    private List<String> others;

    ContainerFormat(String name) {
        this.name = name;
    }

    /** The service does not always return the same String for output formats.
     * This 'other' string fixes the deserialization issues caused by that.
     */
    ContainerFormat(String name, String... others) {
        this.name = name;
        this.others = new ArrayList<String>();
        for (String other : others) {
            this.others.add(other);
        }
    }

    @JsonValue
    @Override
    public String toString() {
        return name;
    }

    public List<String> otherNames() {
        return others;
    }

    @JsonCreator
    public static ContainerFormat fromValue(String other) throws JsonMappingException {
        for (ContainerFormat format : ContainerFormat.values()) {
            if (format.toString().equalsIgnoreCase(other)) {
                return format;
            }
            if (format.otherNames() != null && format.otherNames().contains(other)) {
                return format;
            }
        }
        return UNKNOWN;
    }
}

      

The problem is I am deserializing something that contains "mpeg4" instead of mp4. I am getting this error:

com.fasterxml.jackson.databind.exc.InvalidFormatException: Can not construct instance of com.foo.ContainerFormat from String value 'mpeg4': value not one of declared Enum instance names
 at [Source: N/A; line: -1, column: -1] (through reference chain: com.foo.Info["format"])
    at com.fasterxml.jackson.databind.exc.InvalidFormatException.from(InvalidFormatException.java:55)
    at com.fasterxml.jackson.databind.DeserializationContext.weirdStringException(DeserializationContext.java:650)
    at com.fasterxml.jackson.databind.deser.std.EnumDeserializer.deserialize(EnumDeserializer.java:85)
    at com.fasterxml.jackson.databind.deser.std.EnumDeserializer.deserialize(EnumDeserializer.java:20)
    at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:375)
    at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:98)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:308)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:121)
    at com.fasterxml.jackson.databind.ObjectMapper._readValue(ObjectMapper.java:2769)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:1478)
    at com.fasterxml.jackson.databind.ObjectMapper.treeToValue(ObjectMapper.java:1811)

      

Any pointers on how to fix this?

TIA

+3


source to share


3 answers


I found a good solution based on Florin's answer:

correct configuration with jackson 2.7.0-rc2 (and possibly earlier)

private ObjectMapper createObjectMapper() {
    final ObjectMapper mapper = new ObjectMapper();
    // enable toString method of enums to return the value to be mapped
    mapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
    mapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING);
    return mapper;
}

      



In your enum, you just need to override the toString () method:

public enum EXAMPLE_TYPE {
START("start"),
MORE("more");

    // the value which is used for matching
    // the json node value with this enum
    private final String value;

    SectionType(final String type) {
        value = type;
    }

    @Override
    public String toString() {
        return value;
    }
}

      

You don't need annotations or custom deserializers.

+3


source


Get rid of the and , and instead you only have one field - and serialize one receiver with String name

List<String> other

List<String> names

@JsonValue

public enum ContainerFormat {
//  ....
MP4("mp4", "mpeg4"),
UNKNOWN("null");

private List<String> names;

ContainerFormat(List<String> names) {
    this.names = new ArrayList<String>(names);
}

@JsonValue
public List<String> getNames()
{
    return this.names;
}

@JsonCreator
public static ContainerFormat getContainerFromValue(String value) throws JsonMappingException {
    for (ContainerFormat format : ContainerFormat.values()) {
        if(format.getValues().contains(value))
            return format; 
    }
    return UNKNOWN;
}

      



Alternatively, if you decide to keep your existing code, you can try annotating with otherValues()

@JsonValue

0


source


Well, I found a workaround, one of these flags does the right thing and lets me read that mpeg4 again:

    mapper.configure(org.codehaus.jackson.map.SerializationConfig.Feature.WRITE_NULL_PROPERTIES, false);
    mapper.configure(org.codehaus.jackson.map.SerializationConfig.Feature.WRITE_ENUMS_USING_TO_STRING, true);
    mapper.configure(org.codehaus.jackson.map.DeserializationConfig.Feature.READ_ENUMS_USING_TO_STRING, true);
    mapper.setPropertyNamingStrategy(org.codehaus.jackson.map.PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
    mapper.setSerializationInclusion(org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion.NON_EMPTY);
    mapper.configure(org.codehaus.jackson.map.DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);

      

0


source







All Articles