Skip to content

Instantly share code, notes, and snippets.

@dhke
Created March 13, 2015 11:15
Show Gist options
  • Save dhke/27879e948c3925fee356 to your computer and use it in GitHub Desktop.
Save dhke/27879e948c3925fee356 to your computer and use it in GitHub Desktop.
class CollectionsGenericAbstractTypeResolver
extends AbstractTypeResolver {
public CollectionsGenericAbstractTypeResolver()
{
}
@Override
public JavaType resolveAbstractType(DeserializationConfig config, JavaType type)
{
if (MultiMap.class.isAssignableFrom(type.getRawClass())) {
JavaType keyType = type.getKeyType();
if (keyType == null) {
keyType = TypeFactory.unknownType();
}
JavaType valueType = type.getContentType();
if (valueType == null) {
valueType = TypeFactory.unknownType();
}
return config.getTypeFactory().constructMapLikeType(MultiHashMap.class, keyType, valueType);
} else {
return null;
}
}
}
public class CollectionsGenericDeserializers
extends SimpleDeserializers {
private static final long serialVersionUID = 9185735258872784781L;
@Override
public JsonDeserializer<?> findMapLikeDeserializer(MapLikeType type, DeserializationConfig config,
BeanDescription beanDesc, KeyDeserializer keyDeserializer,
TypeDeserializer elementTypeDeserializer,
JsonDeserializer<?> elementDeserializer) throws JsonMappingException
{
if (MultiMap.class.isAssignableFrom(type.getRawClass())) {
return new MultiMapDeserializer(type, keyDeserializer, elementTypeDeserializer, elementDeserializer);
} else {
return super.findMapLikeDeserializer(type, config, beanDesc, keyDeserializer, elementTypeDeserializer,
elementDeserializer);
}
}
}
public class CollectionsGenericModule
extends SimpleModule {
@Override
public void setupModule(SetupContext context)
{
context.addAbstractTypeResolver(new CollectionsGenericAbstractTypeResolver());
context.addTypeModifier(new CollectionsGenericTypeModifier());
context.addSerializers(new CollectionsGenericSerializers());
context.addDeserializers(new CollectionsGenericDeserializers());
}
}
public class CollectionsGenericSerializers
extends SimpleSerializers {
private static final long serialVersionUID = -1167031439656909741L;
@Override
public JsonSerializer<?> findMapLikeSerializer(SerializationConfig config, MapLikeType type,
BeanDescription beanDesc, JsonSerializer<Object> keySerializer,
TypeSerializer elementTypeSerializer,
JsonSerializer<Object> elementValueSerializer)
{
if (MultiMap.class.isAssignableFrom(type.getRawClass())) {
return new MultiMapSerializer();
} else {
return super.findMapLikeSerializer(config, type, beanDesc, keySerializer, elementTypeSerializer,
elementValueSerializer);
}
}
}
public class CollectionsGenericTypeModifier
extends TypeModifier {
@Override
public JavaType modifyType(JavaType type, Type jdkType, TypeBindings context, TypeFactory typeFactory)
{
final Class<?> rawClass = type.getRawClass();
if (MultiMap.class.isAssignableFrom(rawClass)) {
JavaType keyType = type.containedType(0);
JavaType valueType = type.containedType(1);
if (keyType == null) {
keyType = TypeFactory.unknownType();
}
if (valueType == null) {
valueType = TypeFactory.unknownType();
}
return typeFactory.constructMapLikeType(rawClass, keyType, valueType);
}
return type;
}
}
public class MultiMapDeserializer
extends JsonDeserializer<MultiMap<Object, Object>>
implements ContextualDeserializer {
private final MapLikeType _type;
private final KeyDeserializer _keyDeserializer;
private final TypeDeserializer _elementTypeDeserializer;
private final JsonDeserializer<?> _elementDeserializer;
public MultiMapDeserializer(MapLikeType type, KeyDeserializer keyDeserializer,
TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer)
{
_type = type;
_keyDeserializer = keyDeserializer;
_elementTypeDeserializer = elementTypeDeserializer;
_elementDeserializer = elementDeserializer;
}
protected MultiMap<Object, Object> createMultiMap(DeserializationContext ctxt)
throws JsonProcessingException
{
Class<?> rawClass = _type.getRawClass();
if (Modifier.isAbstract(rawClass.getModifiers())) {
/* XXX - default to MultiHashMap, make this configurable */
rawClass = MultiHashMap.class;
}
try {
return (MultiMap<Object, Object>) rawClass.newInstance();
} catch (IllegalAccessException ex) {
throw ctxt.instantiationException(rawClass, ex);
} catch (InstantiationException ex) {
throw ctxt.instantiationException(rawClass, ex);
}
}
@Override
public MultiMap<Object, Object> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException
{
final MultiMap<Object, Object> multiMap = createMultiMap(ctxt);
expect(p, JsonToken.START_OBJECT, "Multimaps must be JSON objectsf");
while (p.nextToken() != JsonToken.END_OBJECT) {
final Object key;
if (_keyDeserializer != null) {
key = _keyDeserializer.deserializeKey(p.getCurrentName(), ctxt);
} else {
key = p.getCurrentName();
}
p.nextToken();
expect(p, JsonToken.START_ARRAY, "Multimap values should be arrays");
while (p.nextToken() != JsonToken.END_ARRAY) {
final Object value;
if (p.getCurrentToken() == JsonToken.VALUE_NULL) {
value = null;
} else if (_elementTypeDeserializer != null) {
value = _elementDeserializer.deserializeWithType(p, ctxt, _elementTypeDeserializer);
} else {
value = _elementDeserializer.deserialize(p, ctxt);
}
multiMap.put(key, value);
}
}
return multiMap;
}
private void expect(JsonParser jp, JsonToken token, final String messageIfIncorrect) throws IOException
{
if (jp.getCurrentToken() != token) {
throw new JsonMappingException(
"Expecting " + token + ", found " + jp.getCurrentToken() + ": " + messageIfIncorrect,
jp.getCurrentLocation());
}
}
@Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException
{
KeyDeserializer keyDeser = _keyDeserializer;
if (keyDeser == null) {
keyDeser = ctxt.findKeyDeserializer(_type.getKeyType(), property);
}
JsonDeserializer<?> elementDeser = _elementDeserializer;
if (elementDeser == null) {
elementDeser = ctxt.findContextualValueDeserializer(_type.getContentType(), property);
}
TypeDeserializer typeDeser = _elementTypeDeserializer;
if ((typeDeser != null) && (property != null)) {
typeDeser = typeDeser.forProperty(property);
}
return new MultiMapDeserializer(_type, keyDeser, typeDeser, elementDeser);
}
}
public class MultiMapSerializer
extends JsonSerializer<MultiMap> {
@Override
public void serialize(MultiMap value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException
{
serializers.defaultSerializeValue(value.map(), gen);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment