ProtoMapDeserializer.java
/*
* Copyright 2022 Proto Utils Authors
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
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;
}
/** {@inheritDoc} */
@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);
}
}
}