Skip to content

Commit d499f2e

Browse files
committed
Merge pull request #384 from yinzara/master
Added new UNWRAP_SINGLE_VALUE_ARRAYS DeserializationFeature
2 parents ad9a43f + f190cb5 commit d499f2e

15 files changed

+947
-23
lines changed

src/main/java/com/fasterxml/jackson/databind/DeserializationFeature.java

+9
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,15 @@ public enum DeserializationFeature implements ConfigFeature
203203
* Feature is disabled by default.
204204
*/
205205
ACCEPT_SINGLE_VALUE_AS_ARRAY(false),
206+
207+
/**
208+
* Feature that determines whether it is acceptable to coerce single value array (in JSON)
209+
* values to the corresponding value type. This is basically the opposite of the {@link #ACCEPT_SINGLE_VALUE_AS_ARRAY}
210+
* feature. If more than one value is found in the array, a JsonMappingException is thrown.
211+
* <p>
212+
* Feature is disabled by default
213+
*/
214+
UNWRAP_SINGLE_VALUE_ARRAYS(false),
206215

207216
/**
208217
* Feature to allow "unwrapping" root-level JSON value, to match setting of

src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java

+8
Original file line numberDiff line numberDiff line change
@@ -1211,6 +1211,14 @@ public Object deserializeFromArray(JsonParser jp, DeserializationContext ctxt)
12111211
} catch (Exception e) {
12121212
wrapInstantiationProblem(e, ctxt);
12131213
}
1214+
} else if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
1215+
jp.nextToken();
1216+
final Object value = deserialize(jp, ctxt);
1217+
if (jp.nextToken() != JsonToken.END_ARRAY) {
1218+
throw ctxt.wrongTokenException(jp, JsonToken.END_ARRAY,
1219+
"Attempted to unwrap single value array for single '" + _valueClass.getName() + "' value but there was more than a single value in the array");
1220+
}
1221+
return value;
12141222
}
12151223
throw ctxt.mappingException(getBeanClass());
12161224
}

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

+26-11
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
import com.fasterxml.jackson.databind.BeanProperty;
1515
import com.fasterxml.jackson.databind.DeserializationContext;
16+
import com.fasterxml.jackson.databind.DeserializationFeature;
1617
import com.fasterxml.jackson.databind.JsonDeserializer;
1718
import com.fasterxml.jackson.databind.JsonMappingException;
1819
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
@@ -143,19 +144,33 @@ public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanPro
143144
protected java.util.Date _parseDate(JsonParser jp, DeserializationContext ctxt)
144145
throws IOException, JsonProcessingException
145146
{
146-
if (_customFormat != null && jp.getCurrentToken() == JsonToken.VALUE_STRING) {
147-
String str = jp.getText().trim();
148-
if (str.length() == 0) {
149-
return (Date) getEmptyValue();
150-
}
151-
synchronized (_customFormat) {
152-
try {
153-
return _customFormat.parse(str);
154-
} catch (ParseException e) {
155-
throw new IllegalArgumentException("Failed to parse Date value '"+str
156-
+"' (format: \""+_formatString+"\"): "+e.getMessage());
147+
if (_customFormat != null) {
148+
JsonToken t = jp.getCurrentToken();
149+
if (t == JsonToken.VALUE_STRING) {
150+
String str = jp.getText().trim();
151+
if (str.length() == 0) {
152+
return (Date) getEmptyValue();
153+
}
154+
synchronized (_customFormat) {
155+
try {
156+
return _customFormat.parse(str);
157+
} catch (ParseException e) {
158+
throw new IllegalArgumentException("Failed to parse Date value '"+str
159+
+"' (format: \""+_formatString+"\"): "+e.getMessage());
160+
}
157161
}
158162
}
163+
// Issue#381
164+
if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
165+
jp.nextToken();
166+
final Date parsed = _parseDate(jp, ctxt);
167+
t = jp.nextToken();
168+
if (t != JsonToken.END_ARRAY) {
169+
throw ctxt.wrongTokenException(jp, JsonToken.END_ARRAY,
170+
"Attempted to unwrap single value array for single 'java.util.Date' value but there was more than a single value in the array");
171+
}
172+
return parsed;
173+
}
159174
}
160175
return super._parseDate(jp, ctxt);
161176
}

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

+13
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,19 @@ public Enum<?> deserialize(JsonParser jp, DeserializationContext ctxt)
111111
}
112112
return result;
113113
}
114+
115+
// Issue#381
116+
if (curr == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
117+
jp.nextToken();
118+
final Enum<?> parsed = deserialize(jp, ctxt);
119+
curr = jp.nextToken();
120+
if (curr != JsonToken.END_ARRAY) {
121+
throw ctxt.wrongTokenException(jp, JsonToken.END_ARRAY,
122+
"Attempted to unwrap single value array for single '" + _resolver.getEnumClass().getName() + "' value but there was more than a single value in the array");
123+
}
124+
return parsed;
125+
}
126+
114127
throw ctxt.mappingException(_resolver.getEnumClass());
115128
}
116129

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

+11
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
import com.fasterxml.jackson.core.*;
1515
import com.fasterxml.jackson.databind.DeserializationContext;
16+
import com.fasterxml.jackson.databind.DeserializationFeature;
1617
import com.fasterxml.jackson.databind.JavaType;
1718
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
1819
import com.fasterxml.jackson.databind.util.ClassUtil;
@@ -98,6 +99,16 @@ public static Std findDeserializer(Class<?> rawType)
9899
@Override
99100
public T deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException
100101
{
102+
// Issue#381
103+
if (jp.getCurrentToken() == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
104+
jp.nextToken();
105+
final T value = deserialize(jp, ctxt);
106+
if (jp.nextToken() != JsonToken.END_ARRAY) {
107+
throw ctxt.wrongTokenException(jp, JsonToken.END_ARRAY,
108+
"Attempted to unwrap single value array for single '" + _valueClass.getName() + "' value but there was more than a single value in the array");
109+
}
110+
return value;
111+
}
101112
// 22-Sep-2012, tatu: For 2.1, use this new method, may force coercion:
102113
String text = jp.getValueAsString();
103114
if (text != null) { // has String representation

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

+48-4
Original file line numberDiff line numberDiff line change
@@ -238,10 +238,9 @@ public Character deserialize(JsonParser jp, DeserializationContext ctxt)
238238
throws IOException, JsonProcessingException
239239
{
240240
JsonToken t = jp.getCurrentToken();
241-
int value;
242-
241+
243242
if (t == JsonToken.VALUE_NUMBER_INT) { // ok iff ascii value
244-
value = jp.getIntValue();
243+
int value = jp.getIntValue();
245244
if (value >= 0 && value <= 0xFFFF) {
246245
return Character.valueOf((char) value);
247246
}
@@ -254,7 +253,21 @@ public Character deserialize(JsonParser jp, DeserializationContext ctxt)
254253
// actually, empty should become null?
255254
if (text.length() == 0) {
256255
return (Character) getEmptyValue();
256+
}
257+
} else if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
258+
//Issue#381
259+
jp.nextToken();
260+
final Character value = deserialize(jp, ctxt);
261+
if (jp.nextToken() != JsonToken.END_ARRAY) {
262+
throw ctxt.wrongTokenException(jp, JsonToken.END_ARRAY,
263+
"Attempted to unwrap single value array for single '" + _valueClass.getName() + "' value but there was more than a single value in the array"
264+
);
257265
}
266+
return value;
267+
} else if (t == JsonToken.VALUE_NULL && !_valueClass.isPrimitive()) {
268+
//Issue#unreported
269+
// This handles the case where the value required is the Character wrapper class and the token is the null token
270+
return getEmptyValue();
258271
}
259272
throw ctxt.mappingException(_valueClass, t);
260273
}
@@ -436,6 +449,17 @@ public Number deserialize(JsonParser jp, DeserializationContext ctxt)
436449
throw ctxt.weirdStringException(text, _valueClass, "not a valid number");
437450
}
438451
}
452+
453+
if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
454+
jp.nextToken();
455+
final Number value = deserialize(jp, ctxt);
456+
if (jp.nextToken() != JsonToken.END_ARRAY) {
457+
throw ctxt.wrongTokenException(jp, JsonToken.END_ARRAY,
458+
"Attempted to unwrap single value array for single '" + _valueClass.getName() + "' value but there was more than a single value in the array"
459+
);
460+
}
461+
return value;
462+
}
439463
// Otherwise, no can do:
440464
throw ctxt.mappingException(_valueClass, t);
441465
}
@@ -502,10 +526,19 @@ public BigInteger deserialize(JsonParser jp, DeserializationContext ctxt)
502526
* Could do by calling BigDecimal.toBigIntegerExact()
503527
*/
504528
return jp.getDecimalValue().toBigInteger();
529+
} else if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
530+
jp.nextToken();
531+
final BigInteger value = deserialize(jp, ctxt);
532+
if (jp.nextToken() != JsonToken.END_ARRAY) {
533+
throw ctxt.wrongTokenException(jp, JsonToken.END_ARRAY,
534+
"Attempted to unwrap single value array for single 'BigInteger' value but there was more than a single value in the array"
535+
);
536+
}
537+
return value;
505538
} else if (t != JsonToken.VALUE_STRING) { // let's do implicit re-parse
506539
// String is ok too, can easily convert; otherwise, no can do:
507540
throw ctxt.mappingException(_valueClass, t);
508-
}
541+
}
509542
text = jp.getText().trim();
510543
if (text.length() == 0) {
511544
return null;
@@ -547,6 +580,17 @@ public BigDecimal deserialize(JsonParser jp, DeserializationContext ctxt)
547580
throw ctxt.weirdStringException(text, _valueClass, "not a valid representation");
548581
}
549582
}
583+
584+
if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
585+
jp.nextToken();
586+
final BigDecimal value = deserialize(jp, ctxt);
587+
if (jp.nextToken() != JsonToken.END_ARRAY) {
588+
throw ctxt.wrongTokenException(jp, JsonToken.END_ARRAY,
589+
"Attempted to unwrap single value array for single 'BigDecimal' value but there was more than a single value in the array"
590+
);
591+
}
592+
return value;
593+
}
550594
// Otherwise, no can do:
551595
throw ctxt.mappingException(_valueClass, t);
552596
}

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

+11
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.fasterxml.jackson.core.JsonToken;
77

88
import com.fasterxml.jackson.databind.DeserializationContext;
9+
import com.fasterxml.jackson.databind.DeserializationFeature;
910
import com.fasterxml.jackson.databind.JsonMappingException;
1011

1112
public class StackTraceElementDeserializer
@@ -45,7 +46,17 @@ public StackTraceElement deserialize(JsonParser jp, DeserializationContext ctxt)
4546
}
4647
}
4748
return new StackTraceElement(className, methodName, fileName, lineNumber);
49+
} else if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
50+
jp.nextToken();
51+
final StackTraceElement value = deserialize(jp, ctxt);
52+
if (jp.nextToken() != JsonToken.END_ARRAY) {
53+
throw ctxt.wrongTokenException(jp, JsonToken.END_ARRAY,
54+
"Attempted to unwrap single value array for single 'java.lang.StackTraceElement' value but there was more than a single value in the array"
55+
);
56+
}
57+
return value;
4858
}
59+
4960
throw ctxt.mappingException(_valueClass, t);
5061
}
5162
}

0 commit comments

Comments
 (0)