Skip to content

Commit

Permalink
Yet more refactoring for coercion config
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Jun 13, 2020
1 parent 8f13bd2 commit 4b0d5bc
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 73 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2048,8 +2048,9 @@ protected String _shapeForToken(JsonToken t) {
return "Embedded Object";

case VALUE_NUMBER_FLOAT:
return "Floating-point value";
case VALUE_NUMBER_INT:
return "Number value";
return "Integer value";
case VALUE_STRING:
return "String value";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,10 @@ public Byte deserialize(JsonParser p, DeserializationContext ctxt) throws IOExce
if (p.hasToken(JsonToken.VALUE_NUMBER_INT)) {
return p.getByteValue();
}
return _parseByte(p, ctxt, _valueClass);
// if (_primitive) {
// return _parseBytePrimitive(ctxt, p, _valueClass);
// }
return _parseByte(ctxt, p, _valueClass);
}
}

Expand All @@ -288,55 +291,13 @@ public ShortDeserializer(Class<Short> cls, Short nvl)
public Short deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException
{
return _parseShort(p, ctxt);
}

protected Short _parseShort(JsonParser p, DeserializationContext ctxt) throws IOException
{
JsonToken t = p.currentToken();
if (t == JsonToken.VALUE_NUMBER_INT) {
return p.getShortValue();
}
if (t == JsonToken.VALUE_STRING) { // let's do implicit re-parse
String text = p.getText();
CoercionAction act = _checkFromStringCoercion(ctxt, text);
if (act == CoercionAction.AsNull) {
return (Short) getNullValue(ctxt);
}
if (act == CoercionAction.AsEmpty) {
return (Short) getEmptyValue(ctxt);
}
text = text.trim();
if (_hasTextualNull(text)) {
return (Short) _coerceTextualNull(ctxt, _primitive);
}
int value;
try {
value = NumberInput.parseInt(text);
} catch (IllegalArgumentException iae) {
return (Short) ctxt.handleWeirdStringValue(_valueClass, text,
"not a valid Short value");
}
// So far so good: but does it fit?
if (_shortOverflow(value)) {
return (Short) ctxt.handleWeirdStringValue(_valueClass, text,
"overflow, value cannot be represented as 16-bit value");
}
return Short.valueOf((short) value);
}
if (t == JsonToken.VALUE_NUMBER_FLOAT) {
if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_FLOAT_AS_INT)) {
_failDoubleToIntCoercion(p, ctxt, "Short");
}
if (p.hasToken(JsonToken.VALUE_NUMBER_INT)) {
return p.getShortValue();
}
if (t == JsonToken.VALUE_NULL) {
return (Short) _coerceNullToken(ctxt, _primitive);
}
if (t == JsonToken.START_ARRAY) {
return _deserializeFromArray(p, ctxt);
}
return (Short) ctxt.handleUnexpectedToken(_valueClass, p);
// if (_primitive) {
// return _parseShortPrimitive(ctxt, p, _valueClass);
// }
return _parseShort(ctxt, p, _valueClass);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -508,7 +508,7 @@ public byte[] deserialize(JsonParser p, DeserializationContext ctxt) throws IOEx
_verifyNullForPrimitive(ctxt);
value = (byte) 0;
} else {
value = _parseBytePrimitive(ctxt, p, handledType());
value = _parseBytePrimitive(ctxt, p, Byte.TYPE);
}
}
if (ix >= chunk.length) {
Expand Down Expand Up @@ -602,7 +602,7 @@ public short[] deserialize(JsonParser p, DeserializationContext ctxt) throws IOE
_verifyNullForPrimitive(ctxt);
value = (short) 0;
} else {
value = _parseShortPrimitive(p, ctxt);
value = _parseShortPrimitive(ctxt, p, Short.TYPE);
}
if (ix >= chunk.length) {
chunk = builder.appendCompletedChunk(chunk, ix);
Expand All @@ -619,7 +619,7 @@ public short[] deserialize(JsonParser p, DeserializationContext ctxt) throws IOE
@Override
protected short[] handleSingleElementUnwrapped(JsonParser p,
DeserializationContext ctxt) throws IOException {
return new short[] { _parseShortPrimitive(p, ctxt) };
return new short[] { _parseShortPrimitive(ctxt, p, Short.TYPE) };
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,8 @@ protected final boolean _parseBooleanPrimitive(JsonParser p, DeserializationCont
* same as {@link #handledType}, and not necessarily {@code boolean}
* (may be {@code boolean[]} or {@code AtomicBoolean} for example);
* used for coercion config access
*
* @since 2.12
*/
protected final boolean _parseBooleanPrimitive(DeserializationContext ctxt,
JsonParser p, Class<?> targetType)
Expand Down Expand Up @@ -484,21 +486,29 @@ protected boolean _parseBooleanFromInt(JsonParser p, DeserializationContext ctxt
return !"0".equals(p.getText());
}

@Deprecated // since 2.12, use overloaded variant
protected final byte _parseBytePrimitive(JsonParser p, DeserializationContext ctxt) throws IOException {
return _parseBytePrimitive(ctxt, p, Byte.TYPE);
}

/**
* @since 2.12
*/
protected final byte _parseBytePrimitive(DeserializationContext ctxt, JsonParser p,
Class<?> targetType)
throws IOException
{
final JsonToken t = p.currentToken();
if (t == JsonToken.VALUE_NUMBER_INT) return p.getByteValue();
if (t == JsonToken.VALUE_NULL) {
return (Byte) _coerceNullToken(ctxt, true);
_verifyNullForPrimitive(ctxt);
return (byte) 0;
}
if (t == JsonToken.VALUE_STRING) { // let's do implicit re-parse
String text = p.getText();
CoercionAction act = _checkFromStringCoercion(ctxt, text,
LogicalType.Integer, targetType);
if (act == CoercionAction.AsNull) {
// _verifyNullForPrimitiveCoercion(ctxt, text);
return (byte) 0; // no need to check as does not come from `null`, explicit coercion
}
if (act == CoercionAction.AsEmpty) {
Expand Down Expand Up @@ -542,10 +552,13 @@ protected final byte _parseBytePrimitive(DeserializationContext ctxt, JsonParser
_verifyEndArrayForSingle(p, ctxt);
return parsed;
}
return ((Byte) ctxt.handleUnexpectedToken(targetType, p)).byteValue();
return ((Byte) ctxt.handleUnexpectedToken(ctxt.constructType(targetType), p)).byteValue();
}

protected Byte _parseByte(JsonParser p, DeserializationContext ctxt,
/**
* @since 2.12
*/
protected Byte _parseByte(DeserializationContext ctxt, JsonParser p,
Class<?> targetType) throws IOException
{
final JsonToken t = p.currentToken();
Expand All @@ -571,13 +584,13 @@ protected Byte _parseByte(JsonParser p, DeserializationContext ctxt,
try {
value = NumberInput.parseInt(text);
} catch (IllegalArgumentException iae) {
return (Byte) ctxt.handleWeirdStringValue(_valueClass, text,
return (Byte) ctxt.handleWeirdStringValue(targetType, text,
"not a valid Byte value");
}
// So far so good: but does it fit?
// as per [JACKSON-804], allow range up to 255, inclusive
if (_byteOverflow(value)) {
return (Byte) ctxt.handleWeirdStringValue(_valueClass, text,
return (Byte) ctxt.handleWeirdStringValue(targetType, text,
"overflow, value cannot be represented as 8-bit value");
// fall-through for deferred fails
}
Expand All @@ -596,25 +609,114 @@ protected Byte _parseByte(JsonParser p, DeserializationContext ctxt,
if (t == JsonToken.START_ARRAY) { // [databind#381]
return (Byte) _deserializeFromArray(p, ctxt);
}
return (Byte) ctxt.handleUnexpectedToken(_valueClass, p);
return (Byte) ctxt.handleUnexpectedToken(ctxt.constructType(targetType), p);
}

@Deprecated // since 2.12, use overloaded variant
protected final short _parseShortPrimitive(JsonParser p, DeserializationContext ctxt) throws IOException {
return _parseShortPrimitive(ctxt, p, Short.TYPE);
}

protected final short _parseShortPrimitive(JsonParser p, DeserializationContext ctxt)
/**
* @since 2.12
*/
protected final short _parseShortPrimitive(DeserializationContext ctxt, JsonParser p,
Class<?> targetType)
throws IOException
{
final JsonToken t = p.currentToken();
if (t == JsonToken.VALUE_NUMBER_INT) return p.getShortValue();
if (t == JsonToken.VALUE_NULL) {
return (Byte) _coerceNullToken(ctxt, true);
_verifyNullForPrimitive(ctxt);
return (short) 0;
}
if (t == JsonToken.VALUE_STRING) { // let's do implicit re-parse
String text = p.getText();
CoercionAction act = _checkFromStringCoercion(ctxt, text,
LogicalType.Integer, targetType);
if (act == CoercionAction.AsNull) {
return (short) 0; // no need to check as does not come from `null`, explicit coercion
}
if (act == CoercionAction.AsEmpty) {
return (short) 0;
}
text = text.trim();
if (_hasTextualNull(text)) {
_verifyNullForPrimitiveCoercion(ctxt, text);
return (short) 0;
}
int value;
try {
value = NumberInput.parseInt(text);
} catch (IllegalArgumentException iae) {
return (Short) ctxt.handleWeirdStringValue(targetType, text,
"not a valid Short value");
}
if (_shortOverflow(value)) {
return (Short) ctxt.handleWeirdStringValue(targetType, text,
"overflow, value cannot be represented as 16-bit value");
}
return (short) value;
}
// 12-Jun-2020, tatu: For some reason calling `_deserializeFromArray()` won't work so:
if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
p.nextToken();
final short parsed = _parseShortPrimitive(ctxt, p, targetType);
_verifyEndArrayForSingle(p, ctxt);
return parsed;
}
return ((Short) ctxt.handleUnexpectedToken(ctxt.constructType(targetType), p)).shortValue();
}

protected Short _parseShort(DeserializationContext ctxt, JsonParser p,
Class<?> targetType) throws IOException
{
final JsonToken t = p.currentToken();
if (t == JsonToken.VALUE_NUMBER_INT) return p.getShortValue();
if (t == JsonToken.VALUE_NULL) {
return (Short) _coerceNullToken(ctxt, false);
}
if (t == JsonToken.VALUE_STRING) { // let's do implicit re-parse
String text = p.getText();
CoercionAction act = _checkFromStringCoercion(ctxt, text);
if (act == CoercionAction.AsNull) {
return (Short) getNullValue(ctxt);
}
if (act == CoercionAction.AsEmpty) {
return (Short) getEmptyValue(ctxt);
}
text = text.trim();
if (_hasTextualNull(text)) {
return (Short) _coerceTextualNull(ctxt, false);
}
int value;
try {
value = NumberInput.parseInt(text);
} catch (IllegalArgumentException iae) {
return (Short) ctxt.handleWeirdStringValue(_valueClass, text,
"not a valid Short value");
}
// So far so good: but does it fit?
if (_shortOverflow(value)) {
return (Short) ctxt.handleWeirdStringValue(_valueClass, text,
"overflow, value cannot be represented as 16-bit value");
}
return Short.valueOf((short) value);
}
if (t == JsonToken.VALUE_NUMBER_FLOAT) {
CoercionAction act = _checkFloatToIntCoercion(ctxt, p, targetType);
if (act == CoercionAction.AsNull) {
return (Short) getNullValue(ctxt);
}
if (act == CoercionAction.AsEmpty) {
return (Short) getEmptyValue(ctxt);
}
return p.getShortValue();
}
int value = _parseIntPrimitive(p, ctxt);
// So far so good: but does it fit?
if (_shortOverflow(value)) {
Number v = (Number) ctxt.handleWeirdStringValue(_valueClass, String.valueOf(value),
"overflow, value cannot be represented as 16-bit value");
return _nonNullNumber(v).shortValue();
if (t == JsonToken.START_ARRAY) {
return (Short)_deserializeFromArray(p, ctxt);
}
return (short) value;
return (Short) ctxt.handleUnexpectedToken(ctxt.constructType(targetType), p);
}

protected final int _parseIntPrimitive(JsonParser p, DeserializationContext ctxt)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ public void testLegacyFailDoubleToInt() throws Exception
_verifyCoerceFail(READER_LEGACY_FAIL, LongWrapper.class, "{\"l\": 7.7 }");
_verifyCoerceFail(READER_LEGACY_FAIL, long[].class, "[ -1.35 ]");

_verifyCoerceFail(READER_LEGACY_FAIL, Short.class, "0.5");
_verifyCoerceFail(READER_LEGACY_FAIL, Short.TYPE, "-2.5");
_verifyCoerceFail(READER_LEGACY_FAIL, short[].class, "[ -1.35 ]");
_verifyCoerceFailShort(READER_LEGACY_FAIL, Short.class, "0.5");
_verifyCoerceFailShort(READER_LEGACY_FAIL, Short.TYPE, "-2.5");
_verifyCoerceFailShort(READER_LEGACY_FAIL, short[].class, "[ -1.35 ]");

_verifyCoerceFail(READER_LEGACY_FAIL, BigInteger.class, "25236.256");
}
Expand All @@ -74,6 +74,19 @@ private void _verifyCoerceFail(ObjectReader r, Class<?> targetType,
verifyException(e, "Cannot coerce a floating-point");
}
}

private void _verifyCoerceFailShort(ObjectReader r, Class<?> targetType,
String doc) throws Exception
{
try {
r.forType(targetType).readValue(doc);
fail("Should not pass");
} catch (MismatchedInputException e) {
verifyException(e,
"Cannot deserialize value of type `short` from Floating-point value",
"Cannot coerce Floating-point");
}
}

public void testDoubleToLong() throws Exception
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -920,7 +920,7 @@ public void testInvalidStringCoercionFail() throws IOException

// char[] is special, cannot use generalized test here
// _testInvalidStringCoercionFail(char[].class);
_testInvalidStringCoercionFail(short[].class);
_testInvalidStringCoercionFail(short[].class, "short");
_testInvalidStringCoercionFail(int[].class);
_testInvalidStringCoercionFail(long[].class);
_testInvalidStringCoercionFail(float[].class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ public void testSimpleMismatch() throws Exception
mapper.readValue(" 123 ", ArrayNode.class);
fail("Should not pass");
} catch (MismatchedInputException e) {
verifyException(e, "from Number value (token `JsonToken.VALUE_NUMBER_INT`)");
verifyException(e, "from Integer value (token `JsonToken.VALUE_NUMBER_INT`)");
}
}
}

0 comments on commit 4b0d5bc

Please sign in to comment.