@JsonValue on an enum field, when this enum used as map key

enums, jackson, java

Solution

Jackson uses a `MapSerializer` to serialize `Map` types and uses a `StdKeySerializer` to serialize the keys. It's implemented as

@Override
public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider)
    throws IOException, JsonGenerationException
{
    if (value instanceof Date) {
        provider.defaultSerializeDateKey((Date) value, jgen);
    } else {
        jgen.writeFieldName(value.toString());
    }
}

which you can see just gets the `toString()` value of your object. So you can override the `toString()` method in your `enum` as such

public String toString() {
    return getValue();
}

The `@JsonValue` becomes useless.

Alternatively, if you need the `toString()` to remain the same (or default), you can create a custom type that wraps your `Map`

class CustomType {
    private Map<ClusterType, String> map;

    @JsonAnyGetter // necessary to unwrap the Map to the root object, see here: http://jira.codehaus.org/browse/JACKSON-765
    @JsonSerialize(keyUsing = ClusterTypeKeySerializer.class)
    public Map<ClusterType, String> getMap() {
        return map;
    }

    public void setMap(Map<ClusterType, String> map) {
        this.map = map;
    }
}

and uses a custom `JsonSerializer`

class ClusterTypeKeySerializer extends StdSerializer<ClusterType> {

    protected ClusterTypeKeySerializer() {
        super(ClusterType.class);
    }

    @Override
    public void serialize(ClusterType value, JsonGenerator jgen,
            SerializerProvider provider) throws IOException,
            JsonGenerationException {
        jgen.writeFieldName(value.getValue());
    }

}

that uses the `ClusterType#getValue()` method. Again, we don't use `@JsonValue`.

Problem

``` public enum ClusterType { TEMPERATURE("0402"), HUMIDITY("0405"), ENERGY_DETAILS("0702"), SMART_SOCKET_STATUS("0006"), ALARMED("0500"); private String value = null; ClusterType(String byteStr) { this.value = byteStr; } @JsonCreator public static ClusterType fromValue(final String val){ return (ClusterType) CollectionUtils.find(Arrays.asList(ClusterType.values()), new Predicate() { public boolean evaluate(Object object) { ClusterType candidate = (ClusterType) object; return StringUtils.equals(candidate.value, val); } }); } @JsonValue public String getValue(){ return value; } public byte[] get() { return ByteUtils.hexStringToByteArray(value); } public boolean equals(String cluster) { return StringUtils.equals(cluster, value); } } ``` I have the above enumeration with @JsonValue public String getValue(){ return value; } part and a sample test class like... public class Foo { ``` public static void main(String[] args) { try { ObjectMapper objectMapper = new ObjectMapper(); ClusterType []arrayRep = new ClusterType[]{ClusterType.ALARMED, ClusterType.TEMPERATURE}; Map<String, ClusterType> mapRepAsValue = new HashMap<>(); mapRepAsValue.put("1", ClusterType.ALARMED); mapRepAsValue.put("2", ClusterType.TEMPERATURE); Map<ClusterType, String> mapRepAsKey = new HashMap<>(); mapRepAsKey.put(ClusterType.ALARMED, "1"); mapRepAsKey.put(ClusterType.TEMPERATURE, "2"); System.out.println(objectMapper.writeValueAsString(arrayRep)); System.out.println(objectMapper.writeValueAsString(mapRepAsValue)); System.out.println(objectMapper.writeValueAsString(mapRepAsKey)); } catch (JsonProcessingException e) { e.printStackTrace(); } } } ``` This test class prints out ``` ["0500","0402"] {"2":"0402","1":"0500"} {"TEMPERATURE":"2","ALARMED":"1"} ``` @JsonValue is not working when used on an enum field which is a key of map. Is there a way to use this enum as key when serializing maps? Thanks.

Original source