Skip to content

Commit

Permalink
Bit more clean up wrt #53
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Aug 9, 2019
1 parent ce312aa commit 7b3b832
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 88 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -147,23 +147,24 @@ private void requireCollectionOfComparableElements(CollectionType actualType, St
public JsonDeserializer<?> findMapDeserializer(MapType type,
DeserializationConfig config, BeanDescription beanDesc,
KeyDeserializer keyDeserializer,
TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer)
TypeDeserializer valueTypeDeserializer, JsonDeserializer<?> valueDeserializer)
throws JsonMappingException
{
Class<?> raw = type.getRawClass();

// ImmutableXxxMap types?
if (ImmutableMap.class.isAssignableFrom(raw)) {
if (ImmutableSortedMap.class.isAssignableFrom(raw)) {
return new ImmutableSortedMapDeserializer(type, keyDeserializer, elementTypeDeserializer,
elementDeserializer);
return new ImmutableSortedMapDeserializer(type, keyDeserializer,
valueDeserializer, valueTypeDeserializer, null);
}
if (ImmutableBiMap.class.isAssignableFrom(raw)) {
return new ImmutableBiMapDeserializer(type, keyDeserializer, elementTypeDeserializer,
elementDeserializer);
return new ImmutableBiMapDeserializer(type, keyDeserializer,
valueDeserializer, valueTypeDeserializer, null);
}
// Otherwise, plain old ImmutableMap...
return new ImmutableMapDeserializer(type, keyDeserializer, elementTypeDeserializer, elementDeserializer);
return new ImmutableMapDeserializer(type, keyDeserializer,
valueDeserializer, valueTypeDeserializer, null);
}

// XxxBiMap types?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,25 @@
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
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 com.fasterxml.jackson.databind.deser.NullValueProvider;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
import com.fasterxml.jackson.databind.type.MapType;

import com.google.common.collect.ImmutableMap;

abstract class GuavaImmutableMapDeserializer<T extends ImmutableMap<Object, Object>> extends
GuavaMapDeserializer<T> {
GuavaMapDeserializer<T>
{
private static final long serialVersionUID = 2L;

GuavaImmutableMapDeserializer(MapType type, KeyDeserializer keyDeser, TypeDeserializer typeDeser,
JsonDeserializer<?> deser) {
super(type, keyDeser, typeDeser, deser);
GuavaImmutableMapDeserializer(JavaType type, KeyDeserializer keyDeser,
JsonDeserializer<?> valueDeser, TypeDeserializer valueTypeDeser,
NullValueProvider nuller) {
super(type, keyDeser, valueDeser, valueTypeDeser, nuller);
}

protected abstract ImmutableMap.Builder<Object, Object> createBuilder();
Expand All @@ -28,7 +34,7 @@ protected T _deserializeEntries(JsonParser p, DeserializationContext ctxt)
{
final KeyDeserializer keyDes = _keyDeserializer;
final JsonDeserializer<?> valueDes = _valueDeserializer;
final TypeDeserializer typeDeser = _typeDeserializerForValue;
final TypeDeserializer typeDeser = _valueTypeDeserializer;

ImmutableMap.Builder<Object, Object> builder = createBuilder();
for (; p.getCurrentToken() == JsonToken.FIELD_NAME; p.nextToken()) {
Expand All @@ -40,7 +46,14 @@ protected T _deserializeEntries(JsonParser p, DeserializationContext ctxt)
// 28-Nov-2010, tatu: Should probably support "ignorable properties" in future...
Object value;
if (t == JsonToken.VALUE_NULL) {
_handleNull(ctxt, key, _valueDeserializer, builder);
if (!_skipNullValues) {
value = _nullProvider.getNullValue(ctxt);
// 14-Sep-2015, tatu: As per [datatype-guava#52], avoid exception due to null
// TODO: allow reporting problem via a feature, in future?
if (value != null) {
builder.put(key, value);
}
}
continue;
}
if (typeDeser == null) {
Expand All @@ -56,24 +69,4 @@ protected T _deserializeEntries(JsonParser p, DeserializationContext ctxt)
T map = (T) builder.build();
return map;
}

/**
* Overridable helper method called when a JSON null value is encountered.
* Since Guava Maps typically do not allow null values, special handling
* is needed; default is to simply ignore and skip such values, but alternative
* could be to throw an exception.
*/
protected void _handleNull(DeserializationContext ctxt, Object key,
JsonDeserializer<?> valueDeser,
ImmutableMap.Builder<Object, Object> builder) throws IOException
{
// 14-Sep-2015, tatu: As per [datatype-guava#52], avoid exception due to null
// TODO: allow reporting problem via a feature, in future?

// Actually, first, see if there's an alternative to Java null
Object nvl = valueDeser.getNullValue(ctxt);
if (nvl != null) {
builder.put(key, nvl);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@

import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.deser.ContextualKeyDeserializer;
import com.fasterxml.jackson.databind.deser.NullValueProvider;
import com.fasterxml.jackson.databind.deser.std.ContainerDeserializerBase;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
import com.fasterxml.jackson.databind.type.MapType;

public abstract class GuavaMapDeserializer<T>
extends JsonDeserializer<T>
extends ContainerDeserializerBase<T>
implements ContextualDeserializer
{
protected final MapType _mapType;
private static final long serialVersionUID = 2L;

/**
* Key deserializer used, if not null. If null, String from JSON
* content is used as is.
Expand All @@ -30,30 +32,44 @@ public abstract class GuavaMapDeserializer<T>
* If value instances have polymorphic type information, this
* is the type deserializer that can handle it
*/
protected final TypeDeserializer _typeDeserializerForValue;
protected final TypeDeserializer _valueTypeDeserializer;

/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/

protected GuavaMapDeserializer(MapType type, KeyDeserializer keyDeser,
TypeDeserializer typeDeser, JsonDeserializer<?> deser)
protected GuavaMapDeserializer(JavaType type, KeyDeserializer keyDeser,
JsonDeserializer<?> valueDeser, TypeDeserializer valueTypeDeser,
NullValueProvider nuller)
{
_mapType = type;
super(type, nuller, null);
_keyDeserializer = keyDeser;
_typeDeserializerForValue = typeDeser;
_valueDeserializer = deser;
_valueDeserializer = valueDeser;
_valueTypeDeserializer = valueTypeDeser;
}

/**
* Overridable fluent factory method used for creating contextual
* instances.
*/
public abstract GuavaMapDeserializer<T> withResolved(KeyDeserializer keyDeser,
TypeDeserializer typeDeser, JsonDeserializer<?> valueDeser);

JsonDeserializer<?> valueDeser, TypeDeserializer valueTypeDeser,
NullValueProvider nuller);

/*
/**********************************************************
/* Abstract method impl
/**********************************************************
*/

@SuppressWarnings("unchecked")
@Override
public JsonDeserializer<Object> getContentDeserializer() {
return (JsonDeserializer<Object>) _valueDeserializer;
}

/*
/**********************************************************
/* Validation, post-processing
Expand All @@ -70,22 +86,41 @@ public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
BeanProperty property) throws JsonMappingException
{
KeyDeserializer keyDeser = _keyDeserializer;
JsonDeserializer<?> deser = _valueDeserializer;
TypeDeserializer typeDeser = _typeDeserializerForValue;
// Do we need any contextualization?
if ((keyDeser != null) && (deser != null) && (typeDeser == null)) { // nope
return this;
}
JsonDeserializer<?> valueDeser = _valueDeserializer;
TypeDeserializer valueTypeDeser = _valueTypeDeserializer;

// First: fetch and/or contextualize deserializers (key, value, value type)
if (keyDeser == null) {
keyDeser = ctxt.findKeyDeserializer(_mapType.getKeyType(), property);
keyDeser = ctxt.findKeyDeserializer(_containerType.getKeyType(), property);
} else {
if (keyDeser instanceof ContextualKeyDeserializer) {
keyDeser = ((ContextualKeyDeserializer) keyDeser).createContextual(ctxt, property);
}
}
if (deser == null) {
deser = ctxt.findContextualValueDeserializer(_mapType.getContentType(), property);
final JavaType vt = _containerType.getContentType();
if (valueDeser == null) {
valueDeser = ctxt.findContextualValueDeserializer(vt, property);
} else {
valueDeser = ctxt.handleSecondaryContextualization(valueDeser, property, vt);
}
if (typeDeser != null) {
typeDeser = typeDeser.forProperty(property);
if (valueTypeDeser != null) {
valueTypeDeser = valueTypeDeser.forProperty(property);
}

// Then other handlers

NullValueProvider nuller = findContentNullProvider(ctxt, property, valueDeser);

// !!! 08-Aug-2019, tatu: TODO: null skipping? Ignored properties?

if ((_keyDeserializer == keyDeser) && (_valueDeserializer == valueDeser)
&& (_valueTypeDeserializer == valueTypeDeser)
&& (_nullProvider == nuller)
) {
return this;
}
return withResolved(keyDeser, typeDeser, deser);

return withResolved(keyDeser, valueDeser, valueTypeDeser, nuller);
}

/*
Expand Down Expand Up @@ -120,7 +155,7 @@ public T deserialize(JsonParser p, DeserializationContext ctxt)
t = p.nextToken();
}
if (t != JsonToken.FIELD_NAME && t != JsonToken.END_OBJECT) {
return (T) ctxt.handleUnexpectedToken(_mapType.getRawClass(), p);
return (T) ctxt.handleUnexpectedToken(_containerType.getRawClass(), p);
}
return _deserializeEntries(p, ctxt);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
package com.fasterxml.jackson.datatype.guava.deser;

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.KeyDeserializer;
import com.fasterxml.jackson.databind.deser.NullValueProvider;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
import com.fasterxml.jackson.databind.type.MapType;

import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableMap.Builder;

public class ImmutableBiMapDeserializer extends GuavaImmutableMapDeserializer<ImmutableBiMap<Object, Object>> {
public ImmutableBiMapDeserializer(MapType type, KeyDeserializer keyDeser, TypeDeserializer typeDeser,
JsonDeserializer<?> deser) {
super(type, keyDeser, typeDeser, deser);
public class ImmutableBiMapDeserializer
extends GuavaImmutableMapDeserializer<ImmutableBiMap<Object, Object>>
{
private static final long serialVersionUID = 2L;

public ImmutableBiMapDeserializer(JavaType type, KeyDeserializer keyDeser,
JsonDeserializer<?> deser, TypeDeserializer typeDeser,
NullValueProvider nuller) {
super(type, keyDeser, deser, typeDeser, nuller);
}

@Override
Expand All @@ -20,7 +27,8 @@ protected Builder<Object, Object> createBuilder() {

@Override
public GuavaMapDeserializer<ImmutableBiMap<Object, Object>> withResolved(KeyDeserializer keyDeser,
TypeDeserializer typeDeser, JsonDeserializer<?> valueDeser) {
return new ImmutableBiMapDeserializer(_mapType, keyDeser, typeDeser, valueDeser);
JsonDeserializer<?> valueDeser, TypeDeserializer typeDeser,
NullValueProvider nuller) {
return new ImmutableBiMapDeserializer(_containerType, keyDeser, valueDeser, typeDeser, nuller);
}
}
Original file line number Diff line number Diff line change
@@ -1,31 +1,35 @@
package com.fasterxml.jackson.datatype.guava.deser;


import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.KeyDeserializer;
import com.fasterxml.jackson.databind.deser.NullValueProvider;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
import com.fasterxml.jackson.databind.type.MapType;

import com.google.common.collect.ImmutableMap;

public class ImmutableMapDeserializer
extends GuavaImmutableMapDeserializer<ImmutableMap<Object, Object>>
extends GuavaImmutableMapDeserializer<ImmutableMap<Object, Object>>
{
public ImmutableMapDeserializer(MapType type, KeyDeserializer keyDeser,
TypeDeserializer typeDeser, JsonDeserializer<?> deser)
private static final long serialVersionUID = 2L;

public ImmutableMapDeserializer(JavaType type, KeyDeserializer keyDeser,
JsonDeserializer<?> valueDeser, TypeDeserializer valueTypeDeser,
NullValueProvider nuller)
{
super(type, keyDeser, typeDeser, deser);
super(type, keyDeser, valueDeser, valueTypeDeser, nuller);
}

@Override
public ImmutableMapDeserializer withResolved(KeyDeserializer keyDeser,
TypeDeserializer typeDeser, JsonDeserializer<?> valueDeser) {
return new ImmutableMapDeserializer(_mapType, keyDeser,
typeDeser, valueDeser);
JsonDeserializer<?> valueDeser, TypeDeserializer valueTypeDeser,
NullValueProvider nuller) {
return new ImmutableMapDeserializer(_containerType, keyDeser,
valueDeser, valueTypeDeser, nuller);
}

@Override
protected ImmutableMap.Builder<Object, Object> createBuilder() {
return ImmutableMap.builder();
}

}
Original file line number Diff line number Diff line change
@@ -1,27 +1,31 @@
package com.fasterxml.jackson.datatype.guava.deser;

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.KeyDeserializer;
import com.fasterxml.jackson.databind.deser.NullValueProvider;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
import com.fasterxml.jackson.databind.type.MapType;

import com.google.common.collect.ImmutableMap.Builder;
import com.google.common.collect.ImmutableSortedMap;

public class ImmutableSortedMapDeserializer extends GuavaImmutableMapDeserializer<ImmutableSortedMap<Object, Object>> {
public class ImmutableSortedMapDeserializer
extends GuavaImmutableMapDeserializer<ImmutableSortedMap<Object, Object>>
{
private static final long serialVersionUID = 2L;

public ImmutableSortedMapDeserializer(MapType type, KeyDeserializer keyDeser, TypeDeserializer typeDeser,
JsonDeserializer<?> deser) {
super(type, keyDeser, typeDeser, deser);
public ImmutableSortedMapDeserializer(JavaType type, KeyDeserializer keyDeser,
JsonDeserializer<?> valueDeser, TypeDeserializer typeDeser,
NullValueProvider nuller) {
super(type, keyDeser, valueDeser, typeDeser, nuller);
}

@SuppressWarnings("unchecked")
@Override
protected Builder<Object, Object> createBuilder() {
/*
* Not quite sure what to do with sorting/ordering; may require better
* support either via annotations, or via custom serialization (bean
* style that includes ordering aspects)
*/
// Not quite sure what to do with sorting/ordering; may require better
// support either via annotations, or via custom serialization (bean
// style that includes ordering aspects)
@SuppressWarnings("rawtypes")
ImmutableSortedMap.Builder<?, Object> naturalOrder = ImmutableSortedMap.<Comparable, Object>naturalOrder();
ImmutableSortedMap.Builder<Object, Object> builder = (ImmutableSortedMap.Builder<Object, Object>) naturalOrder;
Expand All @@ -30,7 +34,9 @@ protected Builder<Object, Object> createBuilder() {

@Override
public GuavaMapDeserializer<ImmutableSortedMap<Object, Object>> withResolved(KeyDeserializer keyDeser,
TypeDeserializer typeDeser, JsonDeserializer<?> valueDeser) {
return new ImmutableSortedMapDeserializer(_mapType, keyDeser, typeDeser, valueDeser);
JsonDeserializer<?> valueDeser, TypeDeserializer typeDeser,
NullValueProvider nuller)
{
return new ImmutableSortedMapDeserializer(_containerType, keyDeser, valueDeser, typeDeser, nuller);
}
}

0 comments on commit 7b3b832

Please sign in to comment.