Skip to content

Commit

Permalink
Adding "UNWRAP_SINGLE_VALUE_ARRAYS" DeserializationFeature
Browse files Browse the repository at this point in the history
Fixes issue #381. Added new feature that determines whether it is
acceptable to coerce a single value array (in JSON) to the corresponding
value type.
  • Loading branch information
yinzara committed Jan 8, 2014
1 parent ad9a43f commit 0e37a39
Show file tree
Hide file tree
Showing 9 changed files with 561 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,15 @@ public enum DeserializationFeature implements ConfigFeature
* Feature is disabled by default.
*/
ACCEPT_SINGLE_VALUE_AS_ARRAY(false),

/**
* Feature that determines whether it is acceptable to coerce single value array (in JSON)
* values to the corresponding value type. This is basically the opposite of the {@link #ACCEPT_SINGLE_VALUE_AS_ARRAY}
* feature. If more than one value is found in the array, a JsonMappingException is thrown.
* <p>
* Feature is disabled by default
*/
UNWRAP_SINGLE_VALUE_ARRAYS(false),

/**
* Feature to allow "unwrapping" root-level JSON value, to match setting of
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
Expand Down Expand Up @@ -143,19 +144,33 @@ public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanPro
protected java.util.Date _parseDate(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
if (_customFormat != null && jp.getCurrentToken() == JsonToken.VALUE_STRING) {
String str = jp.getText().trim();
if (str.length() == 0) {
return (Date) getEmptyValue();
}
synchronized (_customFormat) {
try {
return _customFormat.parse(str);
} catch (ParseException e) {
throw new IllegalArgumentException("Failed to parse Date value '"+str
+"' (format: \""+_formatString+"\"): "+e.getMessage());
if (_customFormat != null) {
JsonToken t = jp.getCurrentToken();
if (t == JsonToken.VALUE_STRING) {
String str = jp.getText().trim();
if (str.length() == 0) {
return (Date) getEmptyValue();
}
synchronized (_customFormat) {
try {
return _customFormat.parse(str);
} catch (ParseException e) {
throw new IllegalArgumentException("Failed to parse Date value '"+str
+"' (format: \""+_formatString+"\"): "+e.getMessage());
}
}
}
// Issue#381
if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
jp.nextToken();
final Date parsed = _parseDate(jp, ctxt);
t = jp.nextToken();
if (t != JsonToken.END_ARRAY) {
throw ctxt.wrongTokenException(jp, JsonToken.END_ARRAY,
"Attempted to unwrap single value array for single 'java.util.Date' value but there was more than a single value in the array");
}
return parsed;
}
}
return super._parseDate(jp, ctxt);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,19 @@ public Enum<?> deserialize(JsonParser jp, DeserializationContext ctxt)
}
return result;
}

// Issue#381
if (curr == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
jp.nextToken();
final Enum<?> parsed = deserialize(jp, ctxt);
curr = jp.nextToken();
if (curr != JsonToken.END_ARRAY) {
throw ctxt.wrongTokenException(jp, JsonToken.END_ARRAY,
"Attempted to unwrap single value array for single '" + _resolver.getEnumClass().getName() + "' value but there was more than a single value in the array");
}
return parsed;
}

throw ctxt.mappingException(_resolver.getEnumClass());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.fasterxml.jackson.databind.util.ClassUtil;
Expand Down Expand Up @@ -98,6 +99,16 @@ public static Std findDeserializer(Class<?> rawType)
@Override
public T deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException
{
// Issue#381
if (jp.getCurrentToken() == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
jp.nextToken();
final T 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;
}
// 22-Sep-2012, tatu: For 2.1, use this new method, may force coercion:
String text = jp.getValueAsString();
if (text != null) { // has String representation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,10 +238,9 @@ public Character deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
JsonToken t = jp.getCurrentToken();
int value;


if (t == JsonToken.VALUE_NUMBER_INT) { // ok iff ascii value
value = jp.getIntValue();
int value = jp.getIntValue();
if (value >= 0 && value <= 0xFFFF) {
return Character.valueOf((char) value);
}
Expand All @@ -254,7 +253,21 @@ public Character deserialize(JsonParser jp, DeserializationContext ctxt)
// actually, empty should become null?
if (text.length() == 0) {
return (Character) getEmptyValue();
}
} else if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
//Issue#381
jp.nextToken();
final Character 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 (t == JsonToken.VALUE_NULL && !_valueClass.isPrimitive()) {
//Issue#unreported
// This handles the case where the value required is the Character wrapper class and the token is the null token
return getEmptyValue();
}
throw ctxt.mappingException(_valueClass, t);
}
Expand Down Expand Up @@ -436,6 +449,17 @@ public Number deserialize(JsonParser jp, DeserializationContext ctxt)
throw ctxt.weirdStringException(text, _valueClass, "not a valid number");
}
}

if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
jp.nextToken();
final Number 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;
}
// Otherwise, no can do:
throw ctxt.mappingException(_valueClass, t);
}
Expand Down Expand Up @@ -502,10 +526,19 @@ public BigInteger deserialize(JsonParser jp, DeserializationContext ctxt)
* Could do by calling BigDecimal.toBigIntegerExact()
*/
return jp.getDecimalValue().toBigInteger();
} else if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
jp.nextToken();
final BigInteger 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 'BigInteger' value but there was more than a single value in the array"
);
}
return value;
} else if (t != JsonToken.VALUE_STRING) { // let's do implicit re-parse
// String is ok too, can easily convert; otherwise, no can do:
throw ctxt.mappingException(_valueClass, t);
}
}
text = jp.getText().trim();
if (text.length() == 0) {
return null;
Expand Down Expand Up @@ -547,6 +580,17 @@ public BigDecimal deserialize(JsonParser jp, DeserializationContext ctxt)
throw ctxt.weirdStringException(text, _valueClass, "not a valid representation");
}
}

if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
jp.nextToken();
final BigDecimal 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 'BigDecimal' value but there was more than a single value in the array"
);
}
return value;
}
// Otherwise, no can do:
throw ctxt.mappingException(_valueClass, t);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.fasterxml.jackson.core.JsonToken;

import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonMappingException;

public class StackTraceElementDeserializer
Expand Down Expand Up @@ -45,7 +46,17 @@ public StackTraceElement deserialize(JsonParser jp, DeserializationContext ctxt)
}
}
return new StackTraceElement(className, methodName, fileName, lineNumber);
} else if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
jp.nextToken();
final StackTraceElement 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 'java.lang.StackTraceElement' value but there was more than a single value in the array"
);
}
return value;
}

throw ctxt.mappingException(_valueClass, t);
}
}
Loading

0 comments on commit 0e37a39

Please sign in to comment.