Skip to content

Commit

Permalink
Fixde #1106
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Apr 10, 2017
1 parent 9ef3fc8 commit cbe4541
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 23 deletions.
1 change: 1 addition & 0 deletions release-notes/VERSION
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Project: jackson-databind
#1029: Add a way to define property name aliases #1029: Add a way to define property name aliases
#1035: `@JsonAnySetter` assumes key of `String`, does not consider declared type. #1035: `@JsonAnySetter` assumes key of `String`, does not consider declared type.
(reported by Michael F) (reported by Michael F)
#1106: Add `MapperFeature.ALLOW_COERCION_OF_SCALARS` for enabling/disabling coercions
#1284: Make `StdKeySerializers` use new `JsonGenerator.writeFieldId()` for `int`/`long` keys #1284: Make `StdKeySerializers` use new `JsonGenerator.writeFieldId()` for `int`/`long` keys
#1320: Add `ObjectNode.put(String, BigInteger)` #1320: Add `ObjectNode.put(String, BigInteger)`
(proposed by Jan L) (proposed by Jan L)
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ public enum DeserializationFeature implements ConfigFeature
* Feature that can be enabled to allow JSON empty String * Feature that can be enabled to allow JSON empty String
* value ("") to be bound as `null` for POJOs and other structured * value ("") to be bound as `null` for POJOs and other structured
* values ({@link java.util.Map}s, {@link java.util.Collection}s). * values ({@link java.util.Map}s, {@link java.util.Collection}s).
* If disabled, standard POJOs can only be bound from JSON null or * If disabled, standard POJOs can only be bound from JSON `null` or
* JSON Object (standard meaning that no custom deserializers or * JSON Object (standard meaning that no custom deserializers or
* constructors are defined; both of which can add support for other * constructors are defined; both of which can add support for other
* kinds of JSON values); if enabled, empty JSON String can be taken * kinds of JSON values); if enabled, empty JSON String can be taken
Expand All @@ -330,8 +330,9 @@ public enum DeserializationFeature implements ConfigFeature


/** /**
* Feature that can be enabled to allow empty JSON Array * Feature that can be enabled to allow empty JSON Array
* value (that is, <code>[ ]</code>) to be bound to POJOs as null. * value (that is, <code>[ ]</code>) to be bound to POJOs (and
* If disabled, standard POJOs can only be bound from JSON null or * with 2.9, other values too) as `null`.
* If disabled, standard POJOs can only be bound from JSON `null` or
* JSON Object (standard meaning that no custom deserializers or * JSON Object (standard meaning that no custom deserializers or
* constructors are defined; both of which can add support for other * constructors are defined; both of which can add support for other
* kinds of JSON values); if enabled, empty JSON Array will be taken * kinds of JSON values); if enabled, empty JSON Array will be taken
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -235,11 +235,11 @@ protected final Boolean _parseBoolean(JsonParser p, DeserializationContext ctxt)
String text = p.getText().trim(); String text = p.getText().trim();
// [databind#422]: Allow aliases // [databind#422]: Allow aliases
if ("true".equals(text) || "True".equals(text)) { if ("true".equals(text) || "True".equals(text)) {
_verifyStringCoercion(ctxt, text); _verifyStringForScalarCoercion(ctxt, text);
return Boolean.TRUE; return Boolean.TRUE;
} }
if ("false".equals(text) || "False".equals(text)) { if ("false".equals(text) || "False".equals(text)) {
_verifyStringCoercion(ctxt, text); _verifyStringForScalarCoercion(ctxt, text);
return Boolean.FALSE; return Boolean.FALSE;
} }
if (text.length() == 0) { if (text.length() == 0) {
Expand Down Expand Up @@ -298,6 +298,7 @@ protected Byte _parseByte(JsonParser p, DeserializationContext ctxt) throws IOEx
if (len == 0) { if (len == 0) {
return (Byte) _coerceEmptyString(ctxt, _primitive); return (Byte) _coerceEmptyString(ctxt, _primitive);
} }
_verifyStringForScalarCoercion(ctxt, text);
int value; int value;
try { try {
value = NumberInput.parseInt(text); value = NumberInput.parseInt(text);
Expand Down Expand Up @@ -370,6 +371,7 @@ protected Short _parseShort(JsonParser p, DeserializationContext ctxt) throws IO
if (_hasTextualNull(text)) { if (_hasTextualNull(text)) {
return (Short) _coerceTextualNull(ctxt, _primitive); return (Short) _coerceTextualNull(ctxt, _primitive);
} }
_verifyStringForScalarCoercion(ctxt, text);
int value; int value;
try { try {
value = NumberInput.parseInt(text); value = NumberInput.parseInt(text);
Expand Down Expand Up @@ -420,6 +422,7 @@ public Character deserialize(JsonParser p, DeserializationContext ctxt)
{ {
switch (p.getCurrentTokenId()) { switch (p.getCurrentTokenId()) {
case JsonTokenId.ID_NUMBER_INT: // ok iff ascii value case JsonTokenId.ID_NUMBER_INT: // ok iff ascii value
_verifyNumberForScalarCoercion(ctxt, p);
int value = p.getIntValue(); int value = p.getIntValue();
if (value >= 0 && value <= 0xFFFF) { if (value >= 0 && value <= 0xFFFF) {
return Character.valueOf((char) value); return Character.valueOf((char) value);
Expand Down Expand Up @@ -503,6 +506,7 @@ protected final Integer _parseInteger(JsonParser p, DeserializationContext ctxt)
if (_hasTextualNull(text)) { if (_hasTextualNull(text)) {
return (Integer) _coerceTextualNull(ctxt, _primitive); return (Integer) _coerceTextualNull(ctxt, _primitive);
} }
_verifyStringForScalarCoercion(ctxt, text);
try { try {
if (len > 9) { if (len > 9) {
long l = Long.parseLong(text); long l = Long.parseLong(text);
Expand Down Expand Up @@ -572,6 +576,7 @@ protected final Long _parseLong(JsonParser p, DeserializationContext ctxt) throw
if (_hasTextualNull(text)) { if (_hasTextualNull(text)) {
return (Long) _coerceTextualNull(ctxt, _primitive); return (Long) _coerceTextualNull(ctxt, _primitive);
} }
_verifyStringForScalarCoercion(ctxt, text);
// let's allow Strings to be converted too // let's allow Strings to be converted too
try { try {
return Long.valueOf(NumberInput.parseLong(text)); return Long.valueOf(NumberInput.parseLong(text));
Expand Down Expand Up @@ -643,6 +648,7 @@ protected final Float _parseFloat(JsonParser p, DeserializationContext ctxt)
} }
break; break;
} }
_verifyStringForScalarCoercion(ctxt, text);
try { try {
return Float.parseFloat(text); return Float.parseFloat(text);
} catch (IllegalArgumentException iae) { } } catch (IllegalArgumentException iae) { }
Expand Down Expand Up @@ -718,6 +724,7 @@ protected final Double _parseDouble(JsonParser p, DeserializationContext ctxt) t
} }
break; break;
} }
_verifyStringForScalarCoercion(ctxt, text);
try { try {
return parseDouble(text); return parseDouble(text);
} catch (IllegalArgumentException iae) { } } catch (IllegalArgumentException iae) { }
Expand Down Expand Up @@ -797,6 +804,7 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOEx
if (_isNaN(text)) { if (_isNaN(text)) {
return Double.NaN; return Double.NaN;
} }
_verifyStringForScalarCoercion(ctxt, text);
try { try {
if (!_isIntNumber(text)) { if (!_isIntNumber(text)) {
if (ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) { if (ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) {
Expand Down Expand Up @@ -899,6 +907,7 @@ public BigInteger deserialize(JsonParser p, DeserializationContext ctxt) throws
_verifyNullForScalarCoercion(ctxt, text); _verifyNullForScalarCoercion(ctxt, text);
return getNullValue(ctxt); return getNullValue(ctxt);
} }
_verifyStringForScalarCoercion(ctxt, text);
try { try {
return new BigInteger(text); return new BigInteger(text);
} catch (IllegalArgumentException iae) { } } catch (IllegalArgumentException iae) { }
Expand Down Expand Up @@ -939,6 +948,7 @@ public BigDecimal deserialize(JsonParser p, DeserializationContext ctxt)
_verifyNullForScalarCoercion(ctxt, text); _verifyNullForScalarCoercion(ctxt, text);
return getNullValue(ctxt); return getNullValue(ctxt);
} }
_verifyStringForScalarCoercion(ctxt, text);
try { try {
return new BigDecimal(text); return new BigDecimal(text);
} catch (IllegalArgumentException iae) { } } catch (IllegalArgumentException iae) { }
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -193,13 +193,7 @@ protected boolean _parseBooleanFromInt(JsonParser p, DeserializationContext ctxt
// degenerate case of huge integers, legal in JSON. // degenerate case of huge integers, legal in JSON.
// ... this is, on the other hand, probably wrong/sub-optimal for non-JSON // ... this is, on the other hand, probably wrong/sub-optimal for non-JSON
// input. For now, no rea // input. For now, no rea

_verifyNumberForScalarCoercion(ctxt, p);
MapperFeature feat = MapperFeature.ALLOW_COERCION_OF_SCALARS;
if (!ctxt.isEnabled(feat)) {
ctxt.reportInputMismatch(this,
"Can not coerce Number %s (enable `%s.%s` to allow)",
_coercedTypeDesc(), feat.getClass().getSimpleName(), feat.name());
}
// Anyway, note that since we know it's valid (JSON) integer, it can't have // Anyway, note that since we know it's valid (JSON) integer, it can't have
// extra whitespace to trim. // extra whitespace to trim.
return !"0".equals(p.getText()); return !"0".equals(p.getText());
Expand Down Expand Up @@ -832,22 +826,33 @@ protected final void _verifyNullForScalarCoercion(DeserializationContext ctxt, S
} }


// @since 2.9 // @since 2.9
protected void _verifyStringCoercion(DeserializationContext ctxt, String str) throws JsonMappingException protected void _verifyStringForScalarCoercion(DeserializationContext ctxt, String str) throws JsonMappingException
{ {
MapperFeature feat = MapperFeature.ALLOW_COERCION_OF_SCALARS; MapperFeature feat = MapperFeature.ALLOW_COERCION_OF_SCALARS;
if (!ctxt.isEnabled(feat)) { if (!ctxt.isEnabled(feat)) {
ctxt.reportInputMismatch(this, ctxt.reportInputMismatch(this, "Can not coerce String \"%s\" %s (enable `%s.%s` to allow)",
"Can not coerce String \"%s\" %s (enable `%s.%s` to allow)",
str, _coercedTypeDesc(), feat.getClass().getSimpleName(), feat.name()); str, _coercedTypeDesc(), feat.getClass().getSimpleName(), feat.name());
} }
} }


// @since 2.9
protected void _verifyNumberForScalarCoercion(DeserializationContext ctxt, JsonParser p) throws IOException
{
MapperFeature feat = MapperFeature.ALLOW_COERCION_OF_SCALARS;
if (!ctxt.isEnabled(feat)) {
// 31-Mar-2017, tatu: Since we don't know (or this deep, care) about exact type,
// access as a String: may require re-encoding by parser which should be fine
String valueDesc = p.getText();
ctxt.reportInputMismatch(this, "Can not coerce Number (%s) %s (enable `%s.%s` to allow)",
valueDesc, _coercedTypeDesc(), feat.getClass().getSimpleName(), feat.name());
}
}

protected void _reportFailedNullCoerce(DeserializationContext ctxt, boolean state, Enum<?> feature, protected void _reportFailedNullCoerce(DeserializationContext ctxt, boolean state, Enum<?> feature,
String inputDesc) throws JsonMappingException String inputDesc) throws JsonMappingException
{ {
String enableDesc = state ? "enable" : "disable"; String enableDesc = state ? "enable" : "disable";
ctxt.reportInputMismatch(this, ctxt.reportInputMismatch(this, "Can not coerce %s to Null value %s (%s `%s.%s` to allow)",
"Can not coerce %s to Null value %s (%s `%s.%s` to allow)",
inputDesc, _coercedTypeDesc(), enableDesc, feature.getClass().getSimpleName(), feature.name()); inputDesc, _coercedTypeDesc(), enableDesc, feature.getClass().getSimpleName(), feature.name());
} }


Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -138,13 +138,8 @@ public void testStringCoercionOk() throws Exception


public void testStringCoercionFail() throws Exception public void testStringCoercionFail() throws Exception
{ {
// and then expected fails
_verifyCoerceFail(quote("true"), Boolean.TYPE); _verifyCoerceFail(quote("true"), Boolean.TYPE);
_verifyCoerceFail(quote("true"), Boolean.class); _verifyCoerceFail(quote("true"), Boolean.class);
_verifyCoerceFail("1", Boolean.TYPE);
_verifyCoerceFail("1", Boolean.class);

/*
_verifyCoerceFail(quote("123"), Byte.TYPE); _verifyCoerceFail(quote("123"), Byte.TYPE);
_verifyCoerceFail(quote("123"), Byte.class); _verifyCoerceFail(quote("123"), Byte.class);
_verifyCoerceFail(quote("123"), Short.TYPE); _verifyCoerceFail(quote("123"), Short.TYPE);
Expand All @@ -160,7 +155,16 @@ public void testStringCoercionFail() throws Exception


_verifyCoerceFail(quote("123"), BigInteger.class); _verifyCoerceFail(quote("123"), BigInteger.class);
_verifyCoerceFail(quote("123.0"), BigDecimal.class); _verifyCoerceFail(quote("123.0"), BigDecimal.class);
*/ }

public void testMiscCoercionFail() throws Exception
{
// And then we have coercions from more esoteric types too
_verifyCoerceFail("1", Boolean.TYPE);
_verifyCoerceFail("1", Boolean.class);

_verifyCoerceFail("65", Character.class);
_verifyCoerceFail("65", Character.TYPE);
} }


/* /*
Expand Down

0 comments on commit cbe4541

Please sign in to comment.