Skip to content

Commit

Permalink
Start implementing "content nulls" handling for some of collections
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Feb 2, 2017
1 parent 2e9df7d commit ca3ef05
Show file tree
Hide file tree
Showing 12 changed files with 336 additions and 106 deletions.
Expand Up @@ -6,7 +6,7 @@
import java.util.*; import java.util.*;


import com.fasterxml.jackson.annotation.*; import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.annotation.JsonSetter.Nulls;
import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.JsonParser.NumberType; import com.fasterxml.jackson.core.JsonParser.NumberType;


Expand Down Expand Up @@ -920,46 +920,13 @@ protected SettableBeanProperty _resolveMergeAndNullSettings(DeserializationConte
} }


// And after this, see if we require non-standard null handling // And after this, see if we require non-standard null handling
NullValueProvider nuller = _findNullProvider(ctxt, prop, propMetadata); NullValueProvider nuller = findValueNullProvider(ctxt, prop, propMetadata);
if (nuller != null) { if (nuller != null) {
prop = prop.withNullProvider(nuller); prop = prop.withNullProvider(nuller);
} }
return prop; return prop;
} }


// @since 2.9
protected NullValueProvider _findNullProvider(DeserializationContext ctxt,
SettableBeanProperty prop, PropertyMetadata propMetadata)
throws JsonMappingException
{
final Nulls nulls = propMetadata.getValueNulls();
if (nulls != null) {
switch (nulls) {
case FAIL:
return new NullsFailProvider(prop.getFullName(), prop.getType());
case AS_EMPTY:
// Let's first do some sanity checking...
{
JsonDeserializer<?> deser = prop.getValueDeserializer();
// NOTE: although we could use `ValueInstantiator.Gettable` in general,
// let's not since that would prevent being able to use custom impls:
if (deser instanceof BeanDeserializerBase) {
ValueInstantiator vi = ((BeanDeserializerBase) deser).getValueInstantiator();
if (!vi.canCreateUsingDefault()) {
final JavaType type = prop.getType();
ctxt.reportBadDefinition(type,
String.format("Can not create empty instance of %s, no default Creator", type));
}
}
return new NullsAsEmptyProvider(deser);
}
case SKIP: // can't do here
default: // SET/DEFAULT, nothing to do; S
}
}
return null;
}

/* /*
/********************************************************** /**********************************************************
/* Public accessors /* Public accessors
Expand Down
Expand Up @@ -14,6 +14,12 @@
*/ */
public interface NullValueProvider public interface NullValueProvider
{ {
/**
* Marker object used in some context to mark return value that is to indicate
* that `null` should be skipped (instead of being replaced).
*/
public final static Object SKIP_MARKER = new Object();

/** /**
* Method called to possibly convert incoming `null` token (read via * Method called to possibly convert incoming `null` token (read via
* underlying streaming input source) into other value of type accessor * underlying streaming input source) into other value of type accessor
Expand Down
@@ -0,0 +1,34 @@
package com.fasterxml.jackson.databind.deser.impl;

import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.NullValueProvider;
import com.fasterxml.jackson.databind.exc.InvalidNullException;

/**
* Simple {@link NullValueProvider} that will always throw a
* {@link InvalidNullException} when a null is encountered.
*/
public class NullsConstantProvider implements NullValueProvider
{
private final static NullsConstantProvider SKIPPER = new NullsConstantProvider(NullValueProvider.SKIP_MARKER);

protected final Object _nullValue;

public NullsConstantProvider(Object nvl) {
_nullValue = nvl;
}

/**
* Static accessor for a stateless instance that always returns
* {@link NullValueProvider#SKIP_MARKER} marker instance, used to indicate
* that null value is to be skipped instead of replacing it.
*/
public static NullsConstantProvider skipper() {
return SKIPPER;
}

@Override
public Object getNullValue(DeserializationContext ctxt) {
return _nullValue;
}
}
Expand Up @@ -6,6 +6,7 @@


import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.NullValueProvider;
import com.fasterxml.jackson.databind.deser.ValueInstantiator; import com.fasterxml.jackson.databind.deser.ValueInstantiator;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;


Expand Down Expand Up @@ -38,10 +39,11 @@ public ArrayBlockingQueueDeserializer(JavaType containerType,
protected ArrayBlockingQueueDeserializer(JavaType containerType, protected ArrayBlockingQueueDeserializer(JavaType containerType,
JsonDeserializer<Object> valueDeser, TypeDeserializer valueTypeDeser, JsonDeserializer<Object> valueDeser, TypeDeserializer valueTypeDeser,
ValueInstantiator valueInstantiator, ValueInstantiator valueInstantiator,
JsonDeserializer<Object> delegateDeser, Boolean unwrapSingle) JsonDeserializer<Object> delegateDeser,
NullValueProvider nuller, Boolean unwrapSingle)
{ {
super(containerType, valueDeser, valueTypeDeser, valueInstantiator, super(containerType, valueDeser, valueTypeDeser, valueInstantiator, delegateDeser,
delegateDeser, unwrapSingle); nuller, unwrapSingle);
} }


/** /**
Expand All @@ -58,11 +60,13 @@ protected ArrayBlockingQueueDeserializer(ArrayBlockingQueueDeserializer src) {
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected ArrayBlockingQueueDeserializer withResolved(JsonDeserializer<?> dd, protected ArrayBlockingQueueDeserializer withResolved(JsonDeserializer<?> dd,
JsonDeserializer<?> vd, TypeDeserializer vtd, Boolean unwrapSingle) JsonDeserializer<?> vd, TypeDeserializer vtd,
NullValueProvider nuller, Boolean unwrapSingle)
{ {
return new ArrayBlockingQueueDeserializer(_containerType, return new ArrayBlockingQueueDeserializer(_containerType,
(JsonDeserializer<Object>) vd, vtd, (JsonDeserializer<Object>) vd, vtd,
_valueInstantiator, (JsonDeserializer<Object>) dd, unwrapSingle); _valueInstantiator, (JsonDeserializer<Object>) dd,
nuller, unwrapSingle);
} }


/* /*
Expand Down
Expand Up @@ -4,12 +4,12 @@
import java.util.*; import java.util.*;


import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;

import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.*;

import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer; import com.fasterxml.jackson.databind.deser.*;
import com.fasterxml.jackson.databind.deser.UnresolvedForwardReference;
import com.fasterxml.jackson.databind.deser.ValueInstantiator;
import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId.Referring; import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId.Referring;
import com.fasterxml.jackson.databind.deser.std.ContainerDeserializerBase; import com.fasterxml.jackson.databind.deser.std.ContainerDeserializerBase;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
Expand Down Expand Up @@ -54,6 +54,13 @@ public class CollectionDeserializer
*/ */
protected final JsonDeserializer<Object> _delegateDeserializer; protected final JsonDeserializer<Object> _delegateDeserializer;


/**
* Handler we need for dealing with nulls
*
* @since 2.9
*/
protected final NullValueProvider _nullProvider;

/** /**
* Specific override for this instance (from proper, or global per-type overrides) * Specific override for this instance (from proper, or global per-type overrides)
* to indicate whether single value may be taken to mean an unwrapped one-element array * to indicate whether single value may be taken to mean an unwrapped one-element array
Expand All @@ -79,24 +86,26 @@ public CollectionDeserializer(JavaType collectionType,
JsonDeserializer<Object> valueDeser, JsonDeserializer<Object> valueDeser,
TypeDeserializer valueTypeDeser, ValueInstantiator valueInstantiator) TypeDeserializer valueTypeDeser, ValueInstantiator valueInstantiator)
{ {
this(collectionType, valueDeser, valueTypeDeser, valueInstantiator, null, null); this(collectionType, valueDeser, valueTypeDeser, valueInstantiator, null, null, null);
} }


/** /**
* Constructor used when creating contextualized instances. * Constructor used when creating contextualized instances.
*
* @since 2.9
*/ */
protected CollectionDeserializer(JavaType collectionType, protected CollectionDeserializer(JavaType collectionType,
JsonDeserializer<Object> valueDeser, TypeDeserializer valueTypeDeser, JsonDeserializer<Object> valueDeser, TypeDeserializer valueTypeDeser,
ValueInstantiator valueInstantiator, ValueInstantiator valueInstantiator, JsonDeserializer<Object> delegateDeser,
JsonDeserializer<Object> delegateDeser, NullValueProvider nuller, Boolean unwrapSingle)
Boolean unwrapSingle)
{ {
super(collectionType); super(collectionType);
_valueDeserializer = valueDeser; _valueDeserializer = valueDeser;
_valueTypeDeserializer = valueTypeDeser; _valueTypeDeserializer = valueTypeDeser;
_valueInstantiator = valueInstantiator; _valueInstantiator = valueInstantiator;
_delegateDeserializer = delegateDeser; _delegateDeserializer = delegateDeser;
_unwrapSingle = unwrapSingle; _unwrapSingle = unwrapSingle;
_nullProvider = nuller;
} }


/** /**
Expand All @@ -111,21 +120,23 @@ protected CollectionDeserializer(CollectionDeserializer src)
_valueInstantiator = src._valueInstantiator; _valueInstantiator = src._valueInstantiator;
_delegateDeserializer = src._delegateDeserializer; _delegateDeserializer = src._delegateDeserializer;
_unwrapSingle = src._unwrapSingle; _unwrapSingle = src._unwrapSingle;
_nullProvider = src._nullProvider;
} }


/** /**
* Fluent-factory method call to construct contextual instance. * Fluent-factory method call to construct contextual instance.
* *
* @since 2.7 * @since 2.9
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected CollectionDeserializer withResolved(JsonDeserializer<?> dd, protected CollectionDeserializer withResolved(JsonDeserializer<?> dd,
JsonDeserializer<?> vd, TypeDeserializer vtd, JsonDeserializer<?> vd, TypeDeserializer vtd,
Boolean unwrapSingle) NullValueProvider nuller, Boolean unwrapSingle)
{ {
return new CollectionDeserializer(_containerType, return new CollectionDeserializer(_containerType,
(JsonDeserializer<Object>) vd, vtd, (JsonDeserializer<Object>) vd, vtd,
_valueInstantiator, (JsonDeserializer<Object>) dd, unwrapSingle); _valueInstantiator, (JsonDeserializer<Object>) dd,
nuller, unwrapSingle);
} }


/** /**
Expand All @@ -135,7 +146,7 @@ protected CollectionDeserializer withResolved(JsonDeserializer<?> dd,
protected CollectionDeserializer withResolved(JsonDeserializer<?> dd, protected CollectionDeserializer withResolved(JsonDeserializer<?> dd,
JsonDeserializer<?> vd, TypeDeserializer vtd) JsonDeserializer<?> vd, TypeDeserializer vtd)
{ {
return withResolved(dd, vd, vtd, _unwrapSingle); return withResolved(dd, vd, vtd, vd, _unwrapSingle);
} }


// Important: do NOT cache if polymorphic values // Important: do NOT cache if polymorphic values
Expand Down Expand Up @@ -212,7 +223,9 @@ public CollectionDeserializer createContextual(DeserializationContext ctxt,
|| (valueDeser != _valueDeserializer) || (valueDeser != _valueDeserializer)
|| (valueTypeDeser != _valueTypeDeserializer) || (valueTypeDeser != _valueTypeDeserializer)
) { ) {
return withResolved(delegateDeser, valueDeser, valueTypeDeser, unwrapSingle); return withResolved(delegateDeser, valueDeser, valueTypeDeser,
findContentNullProvider(ctxt, property, valueDeser),
unwrapSingle);
} }
return this; return this;
} }
Expand Down Expand Up @@ -293,7 +306,7 @@ public Collection<Object> deserialize(JsonParser p, DeserializationContext ctxt,
try { try {
Object value; Object value;
if (t == JsonToken.VALUE_NULL) { if (t == JsonToken.VALUE_NULL) {
value = valueDes.getNullValue(ctxt); value = _nullProvider.getNullValue(ctxt);
} else if (typeDeser == null) { } else if (typeDeser == null) {
value = valueDes.deserialize(p, ctxt); value = valueDes.deserialize(p, ctxt);
} else { } else {
Expand Down Expand Up @@ -356,7 +369,7 @@ protected final Collection<Object> handleNonArray(JsonParser p, DeserializationC


try { try {
if (t == JsonToken.VALUE_NULL) { if (t == JsonToken.VALUE_NULL) {
value = valueDes.getNullValue(ctxt); value = _nullProvider.getNullValue(ctxt);
} else if (typeDeser == null) { } else if (typeDeser == null) {
value = valueDes.deserialize(p, ctxt); value = valueDes.deserialize(p, ctxt);
} else { } else {
Expand Down

0 comments on commit ca3ef05

Please sign in to comment.