ProtoMapDeserializer.java
package net.morimekta.proto.jackson.adapter;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.KeyDeserializer;
import net.morimekta.collect.MapBuilder;
import net.morimekta.collect.UnmodifiableMap;
import net.morimekta.collect.UnmodifiableSortedMap;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
/**
* Deserialize maps with proto enum keys.
*
* @param <K> The key type.
* @param <V> The value type.
*/
public class ProtoMapDeserializer<K, V> extends JsonDeserializer<Map<K, V>> {
private final Class<?> mapType;
private final KeyDeserializer keyDeserializer;
private final JavaType valueType;
/**
* Instantiate deserializer.
*
* @param mapType The class for the map type.
* @param keyDeserializer The key serializer.
* @param valueType The jackson value type definition.
*/
public ProtoMapDeserializer(Class<?> mapType,
KeyDeserializer keyDeserializer,
JavaType valueType) {
this.mapType = mapType;
this.keyDeserializer = keyDeserializer;
this.valueType = valueType;
}
@Override
@SuppressWarnings("unchecked")
public Map<K, V> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
if (p.currentToken() == JsonToken.VALUE_NULL) {
return null;
}
if (p.currentToken() == JsonToken.START_OBJECT) {
MapBuilder<K, V> builder = UnmodifiableMap.newBuilder();
String next = p.nextFieldName();
while (next != null) {
K key = (K) keyDeserializer.deserializeKey(next, ctxt);
p.nextValue();
V value = ctxt.readValue(p, valueType);
if (key != null && value != null) {
builder.put(key, value);
}
next = p.nextFieldName();
}
return mapWrapper(builder.build());
} else {
throw new JsonParseException(p, "Invalid map start");
}
}
private Map<K, V> mapWrapper(Map<K, V> build) {
if (UnmodifiableMap.class.isAssignableFrom(mapType)) {
return build;
} else if (UnmodifiableSortedMap.class.isAssignableFrom(mapType)) {
return UnmodifiableSortedMap.asSortedMap(build);
} else if (LinkedHashMap.class.isAssignableFrom(mapType)) {
return new LinkedHashMap<>(build);
} else if (SortedMap.class.isAssignableFrom(mapType)) {
return new TreeMap<>(build);
} else {
return new HashMap<>(build);
}
}
}