Here is a generic Map serializer and deserializer that uses a list of key-value pairs, instead of JSON key-value pairs.
[
{
"key": Object,
"value": Object
}...
]
package default;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.impl.MapEntrySerializer;
import com.fasterxml.jackson.databind.ser.std.MapSerializer;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
/**
* Simple Map Serializer<br>
* <br>
* Serializes the map as a list of key-value pairs, instead of as a list of JSON
* key-value pairs (using the default serializer {@link MapSerializer}).
*
* @param <K> the type of keys maintained by the map
* @param <V> the type of mapped values
* @author Gitesh Agarwal (gagarwa)
*/
public class SimpleMapSerializer<K, V> extends StdSerializer<Map<K, V>> {
private static final long serialVersionUID = 1L;
/**
* Default Constructor
*/
public SimpleMapSerializer() {
super(Map.class, true);
}
@Override
public void serialize(Map<K, V> value, JsonGenerator gen, SerializerProvider provider) throws IOException {
List<SimpleEntry<K, V>> listValues = value.entrySet()
.stream()
.map(SimpleEntry::new)
.collect(Collectors.toList());
provider.defaultSerializeValue(listValues, gen);
}
/**
* Simple Entry<br>
* <br>
* Intentionally does not implement the {@link Map.Entry} interface, so as not
* to invoke the default serializer {@link MapEntrySerializer}.
*
* @author Gitesh Agarwal (gagarwa)
*/
protected static class SimpleEntry<K, V> {
private K key;
private V value;
/**
* Default Constructor
*
* @param entry the map entry
*/
public SimpleEntry(Map.Entry<K, V> entry) {
key = entry.getKey();
value = entry.getValue();
}
/**
* @return the key
*/
public K getKey() {
return key;
}
/**
* @return the value
*/
public V getValue() {
return value;
}
}
}
If you don't want to define a custom serializer everytime.
package default;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import com.fasterxml.jackson.databind.ser.std.StdKeySerializers;
import com.fasterxml.jackson.databind.type.MapType;
import com.ibm.response.SimpleMapSerializer;
/**
* Map Serializer Modifier
*
* @author Gitesh Agarwal (gagarwa)
*/
@Configuration
public class MapSerializerModifier extends BeanSerializerModifier {
@Override
@SuppressWarnings("rawtypes")
public JsonSerializer<?> modifyMapSerializer(SerializationConfig config, MapType valueType,
BeanDescription beanDesc, JsonSerializer<?> serializer) {
JsonSerializer keySerializer = StdKeySerializers.getStdKeySerializer(config,
valueType.getKeyType().getRawClass(), false);
if (keySerializer == null)
return new SimpleMapSerializer();
return serializer;
}
/**
* Simple Module Builder, including the map serializer modifier.
*
* @return the module
*/
@Bean
public Module module() {
SimpleModule module = new SimpleModule();
module.setSerializerModifier(new MapSerializerModifier());
return module;
}
}
The deserializer is a little more tricky, because you need to maintain type information for a generic version.
package default;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.KeyDeserializer;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
import com.fasterxml.jackson.databind.deser.std.StdKeyDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.type.MapType;
import com.ibm.request.action.SimpleMapDeserializer;
/**
* Map Deserializer Modifier
*
* @author Gitesh Agarwal (gagarwa)
*/
@Configuration
public class MapDeserializerModifier extends BeanDeserializerModifier {
@Override
@SuppressWarnings("rawtypes")
public JsonDeserializer<?> modifyMapDeserializer(DeserializationConfig config, MapType type,
BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
KeyDeserializer keyDeserializer = StdKeyDeserializer.forType(type.getKeyType().getRawClass());
if (keyDeserializer == null)
return new SimpleMapDeserializer(type, config.getTypeFactory());
return deserializer;
}
/**
* Simple Module Builder, including the map deserializer modifier.
*
* @return the module
*/
@Bean
public Module module() {
SimpleModule module = new SimpleModule();
module.setDeserializerModifier(new MapDeserializerModifier());
return module;
}
}
package default;
package com.ibm.request.action;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.MapDeserializer;
import com.fasterxml.jackson.databind.deser.std.MapEntryDeserializer;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.type.CollectionType;
import com.fasterxml.jackson.databind.type.MapType;
import com.fasterxml.jackson.databind.type.TypeFactory;
/**
* Simple Map Deserializer<br>
* <br>
* Deserializes the map from a list of key-value pairs, instead of from a list
* of JSON key-value pairs (using the default deserializer
* {@link MapDeserializer}).
*
* @param <K> the type of keys maintained by the map
* @param <V> the type of mapped values
* @author Gitesh Agarwal (gagarwa)
*/
public class SimpleMapDeserializer<K, V> extends StdDeserializer<Map<K, V>> {
private static final long serialVersionUID = 1L;
private final CollectionType type;
/**
* Default Constructor
*
* @param type the map type (key, value)
* @param factory the type factory, to create the collection type
*/
public SimpleMapDeserializer(MapType type, TypeFactory factory) {
super(Map.class);
this.type = factory.constructCollectionType(List.class,
factory.constructParametricType(SimpleEntry.class, type.getKeyType(), type.getContentType()));
}
@Override
public Map<K, V> deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
List<SimpleEntry<K, V>> listValues = ctxt.readValue(p, type);
HashMap<K, V> value = new HashMap<>();
listValues.forEach(e -> value.put(e.key, e.value));
return value;
}
/**
* Simple Entry<br>
* <br>
* Intentionally does not implement the {@link Map.Entry} interface, so as not
* to invoke the default deserializer {@link MapEntryDeserializer}.
*
* @author Gitesh Agarwal (gagarwa)
*/
protected static class SimpleEntry<K, V> {
private K key;
private V value;
/**
* Default Constructor
*/
public SimpleEntry() {
}
/**
* @param key the key
*/
public void setKey(K key) {
this.key = key;
}
/**
* @param value the value
*/
public void setValue(V value) {
this.value = value;
}
}
}