Skip to content

Commit b1087a6

Browse files
committed
Merge branch '2.8'
2 parents ab494a1 + 23d49e3 commit b1087a6

File tree

8 files changed

+225
-102
lines changed

8 files changed

+225
-102
lines changed

release-notes/VERSION

+1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ Project: jackson-databind
7171

7272
2.8.8 (not yet released)
7373

74+
(partial) #994: `DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS` only works for POJOs, Maps
7475
#1533: `AsPropertyTypeDeserializer` ignores `DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT`
7576
#1543: JsonFormat.Shape.NUMBER_INT does not work when defined on enum type in 2.8
7677
(reported by Alex P)

src/main/java/com/fasterxml/jackson/databind/deser/std/FromStringDeserializer.java

+5-12
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ public T deserialize(JsonParser p, DeserializationContext ctxt) throws IOExcepti
133133
String text = p.getValueAsString();
134134
if (text != null) { // has String representation
135135
if (text.length() == 0 || (text = text.trim()).length() == 0) {
136-
// 04-Feb-2013, tatu: Usually should become null; but not always
136+
// Usually should become null; but not always
137137
return _deserializeFromEmptyString();
138138
}
139139
Exception cause = null;
@@ -142,10 +142,8 @@ public T deserialize(JsonParser p, DeserializationContext ctxt) throws IOExcepti
142142
if (result != null) {
143143
return result;
144144
}
145-
} catch (IllegalArgumentException iae) {
146-
cause = iae;
147-
} catch (MalformedURLException me) {
148-
cause = me;
145+
} catch (IllegalArgumentException | MalformedURLException e) {
146+
cause = e;
149147
}
150148
String msg = "not a valid textual representation";
151149
if (cause != null) {
@@ -164,13 +162,8 @@ public T deserialize(JsonParser p, DeserializationContext ctxt) throws IOExcepti
164162
}
165163
JsonToken t = p.getCurrentToken();
166164
// [databind#381]
167-
if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
168-
p.nextToken();
169-
final T value = deserialize(p, ctxt);
170-
if (p.nextToken() != JsonToken.END_ARRAY) {
171-
handleMissingEndArrayForSingle(p, ctxt);
172-
}
173-
return value;
165+
if (t == JsonToken.START_ARRAY) {
166+
return _deserializeFromArray(p, ctxt);
174167
}
175168
if (t == JsonToken.VALUE_EMBEDDED_OBJECT) {
176169
// Trivial cases; null to null, instance of type itself returned as is

src/main/java/com/fasterxml/jackson/databind/deser/std/NumberDeserializers.java

+5-36
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ public BooleanDeserializer(Class<Boolean> cls, Boolean nvl)
185185
{
186186
super(cls, nvl, Boolean.FALSE);
187187
}
188-
188+
189189
@Override
190190
public Boolean deserialize(JsonParser j, DeserializationContext ctxt) throws IOException
191191
{
@@ -439,14 +439,7 @@ public Character deserialize(JsonParser p, DeserializationContext ctxt)
439439
case JsonTokenId.ID_NULL:
440440
return (Character) _coerceNullToken(ctxt, _primitive);
441441
case JsonTokenId.ID_START_ARRAY:
442-
if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
443-
p.nextToken();
444-
final Character C = deserialize(p, ctxt);
445-
if (p.nextToken() != JsonToken.END_ARRAY) {
446-
handleMissingEndArrayForSingle(p, ctxt);
447-
}
448-
return C;
449-
}
442+
return _deserializeFromArray(p, ctxt);
450443
default:
451444
}
452445
return (Character) ctxt.handleUnexpectedToken(_valueClass, p);
@@ -857,15 +850,7 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOEx
857850
"not a valid number");
858851
}
859852
case JsonTokenId.ID_START_ARRAY:
860-
if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
861-
p.nextToken();
862-
final Object value = deserialize(p, ctxt);
863-
if (p.nextToken() != JsonToken.END_ARRAY) {
864-
handleMissingEndArrayForSingle(p, ctxt);
865-
}
866-
return value;
867-
}
868-
break;
853+
return _deserializeFromArray(p, ctxt);
869854
}
870855
// Otherwise, no can do:
871856
return ctxt.handleUnexpectedToken(_valueClass, p);
@@ -937,15 +922,7 @@ public BigInteger deserialize(JsonParser p, DeserializationContext ctxt) throws
937922
}
938923
return p.getDecimalValue().toBigInteger();
939924
case JsonTokenId.ID_START_ARRAY:
940-
if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
941-
p.nextToken();
942-
final BigInteger value = deserialize(p, ctxt);
943-
if (p.nextToken() != JsonToken.END_ARRAY) {
944-
handleMissingEndArrayForSingle(p, ctxt);
945-
}
946-
return value;
947-
}
948-
break;
925+
return _deserializeFromArray(p, ctxt);
949926
case JsonTokenId.ID_STRING: // let's do implicit re-parse
950927
String text = p.getText().trim();
951928
// note: no need to call `coerce` as this is never primitive
@@ -999,15 +976,7 @@ public BigDecimal deserialize(JsonParser p, DeserializationContext ctxt)
999976
"not a valid representation");
1000977
}
1001978
case JsonTokenId.ID_START_ARRAY:
1002-
if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
1003-
p.nextToken();
1004-
final BigDecimal value = deserialize(p, ctxt);
1005-
if (p.nextToken() != JsonToken.END_ARRAY) {
1006-
handleMissingEndArrayForSingle(p, ctxt);
1007-
}
1008-
return value;
1009-
}
1010-
break;
979+
return _deserializeFromArray(p, ctxt);
1011980
}
1012981
// Otherwise, no can do:
1013982
return (BigDecimal) ctxt.handleUnexpectedToken(_valueClass, p);

src/main/java/com/fasterxml/jackson/databind/deser/std/StdScalarDeserializer.java

+31
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.fasterxml.jackson.core.*;
66
import com.fasterxml.jackson.databind.DeserializationConfig;
77
import com.fasterxml.jackson.databind.DeserializationContext;
8+
import com.fasterxml.jackson.databind.DeserializationFeature;
89
import com.fasterxml.jackson.databind.JavaType;
910
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
1011
import com.fasterxml.jackson.databind.util.AccessPattern;
@@ -15,6 +16,11 @@
1516
*/
1617
public abstract class StdScalarDeserializer<T> extends StdDeserializer<T>
1718
{
19+
// @since 2.8.8
20+
protected final static int FEATURES_ACCEPT_ARRAYS =
21+
DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS.getMask() |
22+
DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT.getMask();
23+
1824
private static final long serialVersionUID = 1L;
1925

2026
protected StdScalarDeserializer(Class<?> vc) { super(vc); }
@@ -61,4 +67,29 @@ public AccessPattern getNullAccessPattern() {
6167
public AccessPattern getEmptyAccessPattern() {
6268
return AccessPattern.CONSTANT;
6369
}
70+
71+
protected T _deserializeFromArray(JsonParser p, DeserializationContext ctxt) throws IOException
72+
{
73+
JsonToken t;
74+
if (ctxt.hasSomeOfFeatures(FEATURES_ACCEPT_ARRAYS)) {
75+
t = p.nextToken();
76+
if (t == JsonToken.END_ARRAY) {
77+
if (ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT)) {
78+
return getNullValue(ctxt);
79+
}
80+
}
81+
if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
82+
final T parsed = deserialize(p, ctxt);
83+
if (p.nextToken() != JsonToken.END_ARRAY) {
84+
handleMissingEndArrayForSingle(p, ctxt);
85+
}
86+
return parsed;
87+
}
88+
} else {
89+
t = p.getCurrentToken();
90+
}
91+
@SuppressWarnings("unchecked")
92+
T result = (T) ctxt.handleUnexpectedToken(_valueClass, t, p, null);
93+
return result;
94+
}
6495
}

src/main/java/com/fasterxml/jackson/databind/deser/std/StringDeserializer.java

+31-7
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ public class StringDeserializer extends StdScalarDeserializer<String> // non-fin
1212
{
1313
private static final long serialVersionUID = 1L;
1414

15+
// @since 2.8.8
16+
protected final static int FEATURES_ACCEPT_ARRAYS =
17+
DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS.getMask() |
18+
DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT.getMask();
19+
1520
/**
1621
* @since 2.2
1722
*/
@@ -36,13 +41,8 @@ public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOEx
3641
}
3742
JsonToken t = p.getCurrentToken();
3843
// [databind#381]
39-
if ((t == JsonToken.START_ARRAY) && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
40-
p.nextToken();
41-
final String parsed = _parseString(p, ctxt);
42-
if (p.nextToken() != JsonToken.END_ARRAY) {
43-
handleMissingEndArrayForSingle(p, ctxt);
44-
}
45-
return parsed;
44+
if (t == JsonToken.START_ARRAY) {
45+
return _deserializeFromArray(p, ctxt);
4646
}
4747
// need to gracefully handle byte[] data, as base64
4848
if (t == JsonToken.VALUE_EMBEDDED_OBJECT) {
@@ -70,4 +70,28 @@ public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOEx
7070
public String deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException {
7171
return deserialize(p, ctxt);
7272
}
73+
74+
// @since 2.8.8
75+
protected String _deserializeFromArray(JsonParser p, DeserializationContext ctxt) throws IOException
76+
{
77+
JsonToken t;
78+
if (ctxt.hasSomeOfFeatures(FEATURES_ACCEPT_ARRAYS)) {
79+
t = p.nextToken();
80+
if (t == JsonToken.END_ARRAY) {
81+
if (ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT)) {
82+
return getNullValue(ctxt);
83+
}
84+
}
85+
if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
86+
final String parsed = _parseString(p, ctxt);
87+
if (p.nextToken() != JsonToken.END_ARRAY) {
88+
handleMissingEndArrayForSingle(p, ctxt);
89+
}
90+
return parsed;
91+
}
92+
} else {
93+
t = p.getCurrentToken();
94+
}
95+
return (String) ctxt.handleUnexpectedToken(_valueClass, t, p, null);
96+
}
7397
}

src/test/java/com/fasterxml/jackson/databind/deser/TestBeanDeserializer.java

-22
Original file line numberDiff line numberDiff line change
@@ -304,28 +304,6 @@ public void testPOJOFromEmptyString() throws Exception
304304
assertNull(result);
305305
}
306306

307-
// [Databind#540]
308-
public void testPOJOFromEmptyArray() throws Exception
309-
{
310-
final String JSON = " [\n]";
311-
assertFalse(MAPPER.isEnabled(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT));
312-
// first, verify default settings which do not accept empty Array
313-
ObjectMapper mapper = new ObjectMapper();
314-
try {
315-
mapper.readValue(JSON, Bean.class);
316-
fail("Should not accept Empty Array for POJO by default");
317-
} catch (JsonProcessingException e) {
318-
verifyException(e, "START_ARRAY token");
319-
assertValidLocation(e.getLocation());
320-
}
321-
322-
// should be ok to enable dynamically:
323-
ObjectReader r = MAPPER.readerFor(Bean.class)
324-
.with(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT);
325-
Bean result = r.readValue(JSON);
326-
assertNull(result);
327-
}
328-
329307
// [databind#120]
330308
public void testModifyArrayDeserializer() throws Exception
331309
{

src/test/java/com/fasterxml/jackson/databind/deser/jdk/MapDeserializationTest.java

-25
Original file line numberDiff line numberDiff line change
@@ -275,31 +275,6 @@ public void testGenericStringIntMap() throws Exception
275275
assertNull(result.get(""));
276276
}
277277

278-
// [Databind#540]
279-
public void testMapFromEmptyArray() throws Exception
280-
{
281-
final String JSON = " [\n]";
282-
assertFalse(MAPPER.isEnabled(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT));
283-
// first, verify default settings which do not accept empty Array
284-
ObjectMapper mapper = new ObjectMapper();
285-
try {
286-
mapper.readValue(JSON, Map.class);
287-
fail("Should not accept Empty Array for Map by default");
288-
} catch (JsonProcessingException e) {
289-
verifyException(e, "START_ARRAY token");
290-
}
291-
// should be ok to enable dynamically:
292-
ObjectReader r = MAPPER.readerFor(Map.class)
293-
.with(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT);
294-
295-
Map<?,?> result = r.readValue(JSON);
296-
assertNull(result);
297-
298-
EnumMap<?,?> result2 = r.forType(new TypeReference<EnumMap<Key,String>>() { })
299-
.readValue(JSON);
300-
assertNull(result2);
301-
}
302-
303278
/*
304279
/**********************************************************
305280
/* Test methods, maps with enums

0 commit comments

Comments
 (0)