Implement #540
diff --git a/release-notes/VERSION b/release-notes/VERSION
index 775ec3e..b4c5223 100644
--- a/release-notes/VERSION
+++ b/release-notes/VERSION
@@ -9,6 +9,8 @@
#539: Problem with post-procesing of "empty bean" serializer; was not calling
'BeanSerializerModifier.modifySerializer()` for empty beans
(reported by Fabien R, fabienrenaud@github)
+#540: Support deserializing `[]` as null or empty collection when the java type is a not an object
+ (requested by Fabien R, fabienrenaud@github)
#543: Problem resolving self-referential recursive types
(reported by ahgittin@github)
#550: Minor optimization: prune introspection of "well-known" JDK types
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java
index f73bcfe..32a9052 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java
@@ -1192,15 +1192,24 @@
wrapInstantiationProblem(e, ctxt);
}
} else if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
- jp.nextToken();
+ JsonToken t = jp.nextToken();
+ if (t == JsonToken.END_ARRAY && ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT)) {
+ return null;
+ }
final Object value = deserialize(jp, ctxt);
if (jp.nextToken() != JsonToken.END_ARRAY) {
throw ctxt.wrongTokenException(jp, JsonToken.END_ARRAY,
"Attempted to unwrap single value array for single '" + _valueClass.getName() + "' value but there was more than a single value in the array");
}
return value;
+ } else if (ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT)) {
+ JsonToken t = jp.nextToken();
+ if (t == JsonToken.END_ARRAY) {
+ return null;
+ }
+ throw ctxt.mappingException(handledType(), JsonToken.START_ARRAY);
}
- throw ctxt.mappingException(getBeanClass());
+ throw ctxt.mappingException(handledType());
}
public Object deserializeFromEmbedded(JsonParser jp, DeserializationContext ctxt) throws IOException
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumMapDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumMapDeserializer.java
index 1a34b6d..12a9918 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumMapDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumMapDeserializer.java
@@ -19,7 +19,7 @@
extends ContainerDeserializerBase<EnumMap<?,?>>
implements ContextualDeserializer
{
- private static final long serialVersionUID = 4564890642370311174L;
+ private static final long serialVersionUID = 1;
protected final JavaType _mapType;
@@ -114,13 +114,14 @@
/* Actual deserialization
/**********************************************************
*/
-
+
@Override
- public EnumMap<?,?> deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException
+ public EnumMap<?,?> deserialize(JsonParser jp, DeserializationContext ctxt)
+ throws IOException
{
// Ok: must point to START_OBJECT
if (jp.getCurrentToken() != JsonToken.START_OBJECT) {
- throw ctxt.mappingException(EnumMap.class);
+ return _deserializeFromEmpty(jp, ctxt);
}
EnumMap result = constructMap();
final JsonDeserializer<Object> valueDes = _valueDeserializer;
@@ -172,14 +173,15 @@
}
@Override
- public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt, TypeDeserializer typeDeserializer)
+ public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt, TypeDeserializer typeDeserializer)
throws IOException, JsonProcessingException
{
// In future could check current token... for now this should be enough:
return typeDeserializer.deserializeTypedFromObject(jp, ctxt);
}
- private EnumMap<?,?> constructMap() {
+ protected EnumMap<?,?> constructMap() {
return new EnumMap(_enumClass);
}
}
+
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/MapDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/MapDeserializer.java
index a446777..cdad99b 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/MapDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/MapDeserializer.java
@@ -303,7 +303,8 @@
if (t == JsonToken.VALUE_STRING) {
return (Map<Object,Object>) _valueInstantiator.createFromString(ctxt, jp.getText());
}
- throw ctxt.mappingException(getMapClass());
+ // slightly redundant (since String was passed above), but
+ return _deserializeFromEmpty(jp, ctxt);
}
final Map<Object,Object> result = (Map<Object,Object>) _valueInstantiator.createUsingDefault(ctxt);
if (_standardStringKey) {
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDeserializer.java
index e4eeddc..3f9fa66 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDeserializer.java
@@ -800,6 +800,35 @@
}
/**
+ * Helper method that may be used to support fallback for Empty String / Empty Array
+ * non-standard representations; usually for things serialized as JSON Objects.
+ *
+ * @since 2.5
+ */
+ protected T _deserializeFromEmpty(JsonParser jp, DeserializationContext ctxt)
+ throws IOException
+ {
+ JsonToken t = jp.getCurrentToken();
+ if (t == JsonToken.START_ARRAY) {
+ if (ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT)) {
+ t = jp.nextToken();
+ if (t == JsonToken.END_ARRAY) {
+ return null;
+ }
+ throw ctxt.mappingException(handledType(), JsonToken.START_ARRAY);
+ }
+ } else if (t == JsonToken.VALUE_STRING) {
+ if (ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)) {
+ String str = jp.getText().trim();
+ if (str.isEmpty()) {
+ return null;
+ }
+ }
+ }
+ throw ctxt.mappingException(handledType());
+ }
+
+ /**
* Helper method called to determine if we are seeing String value of
* "null", and, further, that it should be coerced to null just like
* null token.
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestMapDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestMapDeserialization.java
index 13a9fd1..c69a8eb 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/TestMapDeserialization.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestMapDeserialization.java
@@ -9,7 +9,6 @@
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.type.TypeReference;
-
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
@@ -18,13 +17,7 @@
public class TestMapDeserialization
extends BaseMapTest
{
- /*
- /**********************************************************
- /* Test classes, enums
- /**********************************************************
- */
-
- enum Key {
+ static enum Key {
KEY1, KEY2, WHATEVER;
}
@@ -84,8 +77,6 @@
ONE, TWO;
}
-
-
/*
/**********************************************************
/* Test methods, untyped (Object valued) maps
@@ -302,6 +293,31 @@
assertNull(result.get(""));
}
+ // [Databind#540]
+ public void testMapFromEmptyArray() throws Exception
+ {
+ final String JSON = " [\n]";
+ assertFalse(MAPPER.isEnabled(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT));
+ // first, verify default settings which do not accept empty Array
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ mapper.readValue(JSON, Map.class);
+ fail("Should not accept Empty Array for Map by default");
+ } catch (JsonProcessingException e) {
+ verifyException(e, "START_ARRAY token");
+ }
+ // should be ok to enable dynamically:
+ ObjectReader r = MAPPER.reader(Map.class)
+ .with(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT);
+
+ Map<?,?> result = r.readValue(JSON);
+ assertNull(result);
+
+ EnumMap<?,?> result2 = r.withType(new TypeReference<EnumMap<Key,String>>() { })
+ .readValue(JSON);
+ assertNull(result2);
+ }
+
/*
/**********************************************************
/* Test methods, maps with enums