I need to serialize and deserialize in JSON format a Java map using the Jackson library.
The map type is Map<Enum<?>, Object>
and its purpose is to store a configuration made by key-value pairs, where keys are enum instances coming from a third party library (hence I do not have control over their definition).
Keys can be enums of different type, whereas values can be int
, double
or String
.
Also, you can assume that different enums have different instance names.
Serialization works fine:
public class Main {
public enum IntParam {I1, I2, I3}
public enum DoubleParam {D1, D2, D3}
public enum StringParam {S1, S2, S3}
public static void main(final String[] args) {
Map<Enum<?>, Object> config = Map.of(
IntParam.I1, 0,
DoubleParam.D2, 1.0,
StringParam.S3, "sss"
);
new ObjectMapper().writeValueAsString(config)
// {"I1": 1, "D2": 1.0, "S3": "sss"}
}
The problem is deserializing the above object:
String json = "{\"I1\": 1, \"D2\": 1.0, \"S3\": \"sss\"}";
new ObjectMapper().readValue(json, new TypeReference<Map<Enum<?>, Object>>())
fails because Jackson cannot know which enum contains instances named I1
, D2
and S3
.
I would like to achieve something like
{
"com.external.company.IntParam.I1": 1,
"com.external.company.DoubleParam.D2": 1.0,
"com.external.company.StringParam.S3": "sss"
}
during serialization and then be able to deserialize it.
Starting from @Chaosfire answer, I achieved exactly what I need.
Code:
@JsonSerialize(keyUsing=MyEnumSerializer.class)
@JsonDeserialize(keyUsing=MyEnumDeserializer.class)
public static class MyParamMap extends HashMap<Enum<?>, Object> {};
public static class MyEnumSerializer extends JsonSerializer<Enum<?>> {
@Override
public void serialize(final Enum<?> value, final JsonGenerator gen, final SerializerProvider serializers) throws IOException {
gen.writeFieldName(value.getClass().getName() + "." + value.name());
}
}
public static class MyEnumDeserializer extends KeyDeserializer {
@Override
public Object deserializeKey(final String key, final DeserializationContext ctxt) {
final int index = key.lastIndexOf(".");
final String className = key.substring(0, index);
try {
final Class<Enum> enumClass = (Class<Enum>) Class.forName(className);
return Enum.valueOf(enumClass, key.substring(index + 1));
} catch (final ClassCastException | ClassNotFoundException e) {
// should not happen
throw new RuntimeException(e);
}
}
}
Usage:
public static void main(final String[] args) throws StreamWriteException, DatabindException, IOException {
MyParamMap params = new MyParamMap();
params.put(IntParam.I1, 1);
params.put(DoubleParam.D2, 1.1);
params.put(StringParam.S3, "sss");
final Path path = Path.of("params.json");
final ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(path.toFile(), params);
params = mapper.readValue(path.toFile(), MyParamMap.class);
System.out.println(params);
}