diff --git a/src/main/java/tools/jackson/databind/JsonNode.java b/src/main/java/tools/jackson/databind/JsonNode.java
index 0d828531a0..7f37df8a35 100644
--- a/src/main/java/tools/jackson/databind/JsonNode.java
+++ b/src/main/java/tools/jackson/databind/JsonNode.java
@@ -461,6 +461,19 @@ public final boolean isBinary() {
return getNodeType() == JsonNodeType.BINARY;
}
+ /**
+ * Method that can be used to check whether this node is a numeric
+ * node ({@link #isNumber} would return true)
+ * AND can be converted without loss to {@code short} (that is, its value fits
+ * in Java's 16-bit signed integer type, {@code short} and
+ * if it is a floating-point number, it does not have fractional part).
+ *
+ * NOTE: this method does not consider possible value type conversion
+ * from non-number types like JSON String into Number; so even if this method returns false,
+ * it is possible that {@link #asShort} could still succeed.
+ */
+ public boolean canConvertToShort() { return false; }
+
/**
* Method that can be used to check whether this node is a numeric
* node ({@link #isNumber} would return true)
@@ -730,6 +743,51 @@ public String asText(String defaultValue) {
*/
public abstract short shortValue();
+ /**
+ * Method similar to {@link #shortValue()}, but that will return specified
+ * {@code defaultValue} if this node cannot be converted to Java {@code short}.
+ *
+ * @param defaultValue Value to return if this node cannot be converted to Java {@code short}
+ *
+ * @return Java {@code short} value this node represents, if possible to accurately represent;
+ * {@code defaultValue} otherwise
+ */
+ public abstract short shortValue(short defaultValue);
+
+ /**
+ * Method similar to {@link #shortValue()} but in addition to coercing Number
+ * values (same as {@link #shortValue()}), will also try to coerce a
+ * couple of additional types (or cases):
+ *
+ * - JSON Floating-point numbers with fractions (ones without fractions
+ * are ok for {@link #shortValue()}) will be truncated to {@code short}
+ * (if (and only if) they fit in {@code short} range).
+ *
+ * - JSON Strings that represent JSON Numbers ("stringified" numbers)
+ *
+ * - JSON Null (converted to {@code 0}))
+ *
+ * - POJO nodes that contain Number values
+ *
+ *
+ *
+ * @return {@code short} value this node represents, if possible to accurately represent
+ *
+ * @throws JsonNodeException if node value cannot be converted to {@code short}
+ */
+ public abstract short asShort();
+
+ /**
+ * Method similar to {@link #shortValue()}, but that will return specified
+ * {@code defaultValue} if this node cannot be converted to {@code short}.
+ *
+ * @param defaultValue Value to return if this node cannot be converted to {@code short}
+ *
+ * @return {@code short} value this node represents, if possible to accurately represent;
+ * {@code defaultValue} otherwise
+ */
+ public abstract short asShort(short defaultValue);
+
// // Scalar access: Numbers, Java int
/**
@@ -1013,6 +1071,45 @@ public String asText(String defaultValue) {
*/
public abstract float floatValue();
+ /**
+ * Method similar to {@link #floatValue()}, but that will return specified
+ * {@code defaultValue} if this node cannot be converted to {@code float}.
+ *
+ * @param defaultValue Value to return if this node cannot be converted to {@code float}
+ *
+ * @return {@code float} value this node represents, if possible to accurately represent;
+ * {@code defaultValue} otherwise
+ */
+ public abstract float floatValue(float defaultValue);
+
+ /**
+ * Method similar to {@link #floatValue()} but in addition to coercing Number
+ * values will also try coerce couple of additional types:
+ *
+ * - JSON String that represents JSON Numbers ("stringified" numbers)
+ *
+ * - JSON Null (converted to {@code 0.0f})
+ *
+ * - POJO nodes that contain Number values
+ *
+ *
+ *
+ *
+ * @return {@code float} value this node represents, if possible to accurately represent
+ *
+ * @throws JsonNodeException if node value cannot be converted to {@code float}
+ */
+ public abstract float asFloat();
+
+ /**
+ * Method similar to {@link #asFloat()}, but that will return {@code defaultValue}
+ * if this node cannot be coerced to {@code float}.
+ *
+ * @return {@code float} value this node represents,
+ * if possible to accurately represent; {@code defaultValue} otherwise
+ */
+ public abstract float asFloat(float defaultValue);
+
// // Scalar access: Numbers, Java double
/**
diff --git a/src/main/java/tools/jackson/databind/node/BaseJsonNode.java b/src/main/java/tools/jackson/databind/node/BaseJsonNode.java
index 9dea2cc8dc..1e7b287875 100644
--- a/src/main/java/tools/jackson/databind/node/BaseJsonNode.java
+++ b/src/main/java/tools/jackson/databind/node/BaseJsonNode.java
@@ -70,6 +70,23 @@ public short shortValue() {
return _reportCoercionFail("shortValue()", Short.TYPE, "value type not numeric");
}
+ @Override
+ public short shortValue(short defaultValue) {
+ // Overridden by NumericNode, for other types return default
+ return defaultValue;
+ }
+
+ @Override
+ public short asShort() {
+ return _reportCoercionFail("asShort()", Short.TYPE, "value type not numeric");
+ }
+
+ @Override
+ public short asShort(short defaultValue) {
+ // Overridden by NumericNode, for other types return default
+ return defaultValue;
+ }
+
@Override
public int intValue() {
return _reportCoercionFail("intValue()", Integer.TYPE, "value type not numeric");
@@ -177,6 +194,23 @@ public float floatValue() {
return _reportCoercionFail("floatValue()", Float.TYPE, "value type not numeric");
}
+ @Override
+ public float floatValue(float defaultValue) {
+ // Overridden by NumericNode, for other types return default
+ return defaultValue;
+ }
+
+ @Override
+ public float asFloat() {
+ return _reportCoercionFail("asFloat()", Float.TYPE, "value type not numeric");
+ }
+
+ @Override
+ public float asFloat(float defaultValue) {
+ // Overridden by NumericNode, for other types return default
+ return defaultValue;
+ }
+
@Override
public double doubleValue() {
return _reportCoercionFail("doubleValue()", Double.TYPE, "value type not numeric");
diff --git a/src/main/java/tools/jackson/databind/node/BigIntegerNode.java b/src/main/java/tools/jackson/databind/node/BigIntegerNode.java
index e44ea081f7..80fbb2eca7 100644
--- a/src/main/java/tools/jackson/databind/node/BigIntegerNode.java
+++ b/src/main/java/tools/jackson/databind/node/BigIntegerNode.java
@@ -19,11 +19,6 @@ public class BigIntegerNode
{
private static final long serialVersionUID = 3L;
- private final static BigInteger MIN_INTEGER = BigInteger.valueOf(Integer.MIN_VALUE);
- private final static BigInteger MAX_INTEGER = BigInteger.valueOf(Integer.MAX_VALUE);
- private final static BigInteger MIN_LONG = BigInteger.valueOf(Long.MIN_VALUE);
- private final static BigInteger MAX_LONG = BigInteger.valueOf(Long.MAX_VALUE);
-
final protected BigInteger _value;
/*
@@ -72,6 +67,32 @@ public Number numberValue() {
return _value;
}
+ @Override
+ public short shortValue() {
+ if (_inShortRange()) {
+ return (short) _value.intValue();
+ }
+ return _reportShortCoercionRangeFail("shortValue()");
+ }
+
+ @Override
+ public short shortValue(short defaultValue) {
+ return _inShortRange() ? (short) _value.intValue() : defaultValue;
+ }
+
+ @Override
+ public short asShort() {
+ if (_inShortRange()) {
+ return (short) _value.intValue();
+ }
+ return _reportShortCoercionRangeFail("asShort()");
+ }
+
+ @Override
+ public short asShort(short defaultValue) {
+ return _inShortRange() ? (short) _value.intValue() : defaultValue;
+ }
+
@Override
public int intValue() {
if (_inIntRange()) {
@@ -166,6 +187,27 @@ public float floatValue() {
return _reportFloatCoercionRangeFail("floatValue()");
}
+ @Override
+ public float floatValue(float defaultValue) {
+ float f = _asFloatValueUnchecked();
+ return (Float.isFinite(f)) ? f : defaultValue;
+ }
+
+ @Override
+ public float asFloat() {
+ float f = _asFloatValueUnchecked();
+ if (Float.isFinite(f)) {
+ return f;
+ }
+ return _reportFloatCoercionRangeFail("asFloat()");
+ }
+
+ @Override
+ public float asFloat(float defaultValue) {
+ float f = _asFloatValueUnchecked();
+ return (Float.isFinite(f)) ? f : defaultValue;
+ }
+
@Override
public double doubleValue() {
double d = _asDoubleValueUnchecked();
@@ -252,23 +294,20 @@ protected double _asDoubleValueUnchecked() {
@Override
protected boolean _inShortRange() {
- if (_inIntRange()) {
- int v = _value.intValue();
- return (v >= Short.MIN_VALUE && v <= Short.MAX_VALUE);
- }
- return false;
+ return (_value.compareTo(BI_MIN_SHORT) >= 0)
+ && (_value.compareTo(BI_MAX_SHORT) <= 0);
}
@Override
public boolean _inIntRange() {
- return (_value.compareTo(MIN_INTEGER) >= 0)
- && (_value.compareTo(MAX_INTEGER) <= 0);
+ return (_value.compareTo(BI_MIN_INTEGER) >= 0)
+ && (_value.compareTo(BI_MAX_INTEGER) <= 0);
}
@Override
protected boolean _inLongRange() {
- return (_value.compareTo(MIN_LONG) >= 0)
- && (_value.compareTo(MAX_LONG) <= 0);
+ return (_value.compareTo(BI_MIN_LONG) >= 0)
+ && (_value.compareTo(BI_MAX_LONG) <= 0);
}
/*
diff --git a/src/main/java/tools/jackson/databind/node/BooleanNode.java b/src/main/java/tools/jackson/databind/node/BooleanNode.java
index e21c0f64de..09e252a7b7 100644
--- a/src/main/java/tools/jackson/databind/node/BooleanNode.java
+++ b/src/main/java/tools/jackson/databind/node/BooleanNode.java
@@ -111,11 +111,6 @@ protected String _asString() {
return _value ? "true" : "false";
}
- @Override
- public long asLong(long defaultValue) {
- return _value ? 1L : 0L;
- }
-
/*
/**********************************************************************
/* Overridden JsonNode methods, other
diff --git a/src/main/java/tools/jackson/databind/node/DecimalNode.java b/src/main/java/tools/jackson/databind/node/DecimalNode.java
index ea92a96a6f..1b4d7f22af 100644
--- a/src/main/java/tools/jackson/databind/node/DecimalNode.java
+++ b/src/main/java/tools/jackson/databind/node/DecimalNode.java
@@ -20,13 +20,6 @@ public class DecimalNode
public static final DecimalNode ZERO = new DecimalNode(BigDecimal.ZERO);
- private final static BigDecimal MIN_SHORT = BigDecimal.valueOf(Short.MIN_VALUE);
- private final static BigDecimal MAX_SHORT = BigDecimal.valueOf(Short.MAX_VALUE);
- private final static BigDecimal MIN_INTEGER = BigDecimal.valueOf(Integer.MIN_VALUE);
- private final static BigDecimal MAX_INTEGER = BigDecimal.valueOf(Integer.MAX_VALUE);
- private final static BigDecimal MIN_LONG = BigDecimal.valueOf(Long.MIN_VALUE);
- private final static BigDecimal MAX_LONG = BigDecimal.valueOf(Long.MAX_VALUE);
-
final protected BigDecimal _value;
/*
@@ -87,6 +80,33 @@ public float floatValue() {
return _reportFloatCoercionRangeFail("floatValue()");
}
+ @Override
+ public float floatValue(float defaultValue) {
+ float f = _value.floatValue();
+ if (Float.isFinite(f)) {
+ return f;
+ }
+ return defaultValue;
+ }
+
+ @Override
+ public float asFloat() {
+ float f = _value.floatValue();
+ if (Float.isFinite(f)) {
+ return f;
+ }
+ return _reportFloatCoercionRangeFail("asFloat()");
+ }
+
+ @Override
+ public float asFloat(float defaultValue) {
+ float f = _value.floatValue();
+ if (Float.isFinite(f)) {
+ return f;
+ }
+ return defaultValue;
+ }
+
@Override
public double doubleValue() {
double d = _value.doubleValue();
@@ -201,17 +221,17 @@ protected boolean _hasFractionalPart() {
@Override
protected boolean _inShortRange() {
- return (_value.compareTo(MIN_SHORT) >= 0) && (_value.compareTo(MAX_SHORT) <= 0);
+ return (_value.compareTo(BD_MIN_SHORT) >= 0) && (_value.compareTo(BD_MAX_SHORT) <= 0);
}
@Override
protected boolean _inIntRange() {
- return (_value.compareTo(MIN_INTEGER) >= 0) && (_value.compareTo(MAX_INTEGER) <= 0);
+ return _inLongRange() && (_value.compareTo(BD_MIN_INTEGER) >= 0) && (_value.compareTo(BD_MAX_INTEGER) <= 0);
}
@Override
protected boolean _inLongRange() {
- return (_value.compareTo(MIN_LONG) >= 0) && (_value.compareTo(MAX_LONG) <= 0);
+ return (_value.compareTo(BD_MIN_LONG) >= 0) && (_value.compareTo(BD_MAX_LONG) <= 0);
}
/*
diff --git a/src/main/java/tools/jackson/databind/node/DoubleNode.java b/src/main/java/tools/jackson/databind/node/DoubleNode.java
index 3804f6166e..d2334e7d91 100644
--- a/src/main/java/tools/jackson/databind/node/DoubleNode.java
+++ b/src/main/java/tools/jackson/databind/node/DoubleNode.java
@@ -77,6 +77,33 @@ public float floatValue() {
return _reportFloatCoercionRangeFail("floatValue()");
}
+ @Override
+ public float floatValue(float defaultValue) {
+ float f = (float) _value;
+ if (Float.isFinite(f)) {
+ return f;
+ }
+ return defaultValue;
+ }
+
+ @Override
+ public float asFloat() {
+ float f = (float) _value;
+ if (Float.isFinite(f)) {
+ return f;
+ }
+ return _reportFloatCoercionRangeFail("asFloat()");
+ }
+
+ @Override
+ public float asFloat(float defaultValue) {
+ float f = (float) _value;
+ if (Float.isFinite(f)) {
+ return f;
+ }
+ return defaultValue;
+ }
+
@Override
public double doubleValue() {
return _value;
diff --git a/src/main/java/tools/jackson/databind/node/FloatNode.java b/src/main/java/tools/jackson/databind/node/FloatNode.java
index b8c0609b1b..467ea2a1e4 100644
--- a/src/main/java/tools/jackson/databind/node/FloatNode.java
+++ b/src/main/java/tools/jackson/databind/node/FloatNode.java
@@ -67,6 +67,21 @@ public float floatValue() {
return _value;
}
+ @Override
+ public float floatValue(float defaultValue) {
+ return _value;
+ }
+
+ @Override
+ public float asFloat() {
+ return _value;
+ }
+
+ @Override
+ public float asFloat(float defaultValue) {
+ return _value;
+ }
+
@Override
public double doubleValue() {
return _value;
diff --git a/src/main/java/tools/jackson/databind/node/IntNode.java b/src/main/java/tools/jackson/databind/node/IntNode.java
index bdad938678..c5fde896b4 100644
--- a/src/main/java/tools/jackson/databind/node/IntNode.java
+++ b/src/main/java/tools/jackson/databind/node/IntNode.java
@@ -82,6 +82,32 @@ public Number numberValue() {
return Integer.valueOf(_value);
}
+ @Override
+ public short shortValue() {
+ if (_inShortRange()) {
+ return (short) _value;
+ }
+ return _reportShortCoercionRangeFail("shortValue()");
+ }
+
+ @Override
+ public short shortValue(short defaultValue) {
+ return _inShortRange() ? (short) _value : defaultValue;
+ }
+
+ @Override
+ public short asShort() {
+ if (_inShortRange()) {
+ return (short) _value;
+ }
+ return _reportShortCoercionRangeFail("asShort()");
+ }
+
+ @Override
+ public short asShort(short defaultValue) {
+ return _inShortRange() ? (short) _value : defaultValue;
+ }
+
@Override
public int intValue() { return _value; }
diff --git a/src/main/java/tools/jackson/databind/node/LongNode.java b/src/main/java/tools/jackson/databind/node/LongNode.java
index 052375494f..9cfda12086 100644
--- a/src/main/java/tools/jackson/databind/node/LongNode.java
+++ b/src/main/java/tools/jackson/databind/node/LongNode.java
@@ -62,6 +62,32 @@ public Number numberValue() {
return Long.valueOf(_value);
}
+ @Override
+ public short shortValue() {
+ if (_inShortRange()) {
+ return (short) _value;
+ }
+ return _reportShortCoercionRangeFail("shortValue()");
+ }
+
+ @Override
+ public short shortValue(short defaultValue) {
+ return _inShortRange() ? (short) _value : defaultValue;
+ }
+
+ @Override
+ public short asShort() {
+ if (_inShortRange()) {
+ return (short) _value;
+ }
+ return _reportShortCoercionRangeFail("asShort()");
+ }
+
+ @Override
+ public short asShort(short defaultValue) {
+ return _inShortRange() ? (short) _value : defaultValue;
+ }
+
@Override
public int intValue() {
if (_inIntRange()) {
diff --git a/src/main/java/tools/jackson/databind/node/NullNode.java b/src/main/java/tools/jackson/databind/node/NullNode.java
index 0c9fc04a89..177e9bf7c4 100644
--- a/src/main/java/tools/jackson/databind/node/NullNode.java
+++ b/src/main/java/tools/jackson/databind/node/NullNode.java
@@ -65,6 +65,18 @@ protected String _asString() {
/**********************************************************************
*/
+ // `shortValue()` (etc) fine as defaults (fail); but need to override `asShort()`
+
+ @Override
+ public short asShort() {
+ return 0;
+ }
+
+ @Override
+ public short asShort(short defaultValue) {
+ return 0;
+ }
+
// `intValue()` (etc) fine as defaults (fail); but need to override `asInt()`
@Override
@@ -112,6 +124,18 @@ public Optional asBigIntegerOpt() {
return Optional.of(asBigInteger());
}
+ // `floatValue()` (etc) fine as defaults (fail); but need to override `asFloat()`
+
+ @Override
+ public float asFloat() {
+ return 0.0f;
+ }
+
+ @Override
+ public float asFloat(float defaultValue) {
+ return asFloat();
+ }
+
// `doubleValue()` (etc) fine as defaults (fail); but need to override `asDouble()`
@Override
diff --git a/src/main/java/tools/jackson/databind/node/NumericFPNode.java b/src/main/java/tools/jackson/databind/node/NumericFPNode.java
index ac2346e737..eb532918a3 100644
--- a/src/main/java/tools/jackson/databind/node/NumericFPNode.java
+++ b/src/main/java/tools/jackson/databind/node/NumericFPNode.java
@@ -29,6 +29,11 @@ public abstract class NumericFPNode extends NumericNode
@Override
public final boolean isFloatingPointNumber() { return true; }
+ @Override
+ public final boolean canConvertToShort() {
+ return canConvertToExactIntegral() && _inShortRange();
+ }
+
@Override
public final boolean canConvertToInt() {
return canConvertToExactIntegral() && _inIntRange();
@@ -55,6 +60,9 @@ public final boolean canConvertToExactIntegral() {
@Override
public final short shortValue() {
if (!_inShortRange()) {
+ if (isNaN()) {
+ _reportIntCoercionNaNFail("shortValue()");
+ }
return _reportShortCoercionRangeFail("shortValue()");
}
if (_hasFractionalPart()) {
@@ -63,6 +71,33 @@ public final short shortValue() {
return _asShortValueUnchecked();
}
+ @Override
+ public final short shortValue(short defaultValue) {
+ if (!_inShortRange() || _hasFractionalPart()) {
+ return defaultValue;
+ }
+ return _asShortValueUnchecked();
+ }
+
+ @Override
+ public short asShort() {
+ if (!_inShortRange()) {
+ if (isNaN()) {
+ _reportIntCoercionNaNFail("asShort()");
+ }
+ return _reportShortCoercionRangeFail("asShort()");
+ }
+ return _asShortValueUnchecked();
+ }
+
+ @Override
+ public short asShort(short defaultValue) {
+ if (!_inShortRange()) {
+ return defaultValue;
+ }
+ return _asShortValueUnchecked();
+ }
+
@Override
public final int intValue() {
if (!_inIntRange()) {
diff --git a/src/main/java/tools/jackson/databind/node/NumericIntNode.java b/src/main/java/tools/jackson/databind/node/NumericIntNode.java
index 36eb3375fc..0b59297032 100644
--- a/src/main/java/tools/jackson/databind/node/NumericIntNode.java
+++ b/src/main/java/tools/jackson/databind/node/NumericIntNode.java
@@ -31,6 +31,11 @@ public abstract class NumericIntNode extends NumericNode
@Override
public final boolean isNaN() { return false; }
+ @Override final
+ public boolean canConvertToShort() {
+ return _inShortRange();
+ }
+
@Override final
public boolean canConvertToInt() {
return _inIntRange();
@@ -47,13 +52,6 @@ public boolean canConvertToLong() {
/**********************************************************************
*/
- @Override
- public short shortValue() {
- if (_inShortRange()) {
- return (short) _asIntValueUnchecked();
- }
- return _reportShortCoercionRangeFail("shortValue()");
- }
// Sub-classes need to define this; but with that can implement other 5 methods
@@ -85,15 +83,28 @@ public Optional asBigIntegerOpt() {
return bigIntegerValueOpt();
}
- // Float is simple
+ // Float and Double handling straight-forward for all Integral types except BigInteger
+ // (which needs range checks and overrides these implementations)
@Override
public float floatValue() {
return _asFloatValueUnchecked();
}
- // Double handling straight-forward for all Integral types except BigInteger
- // (which needs range checks and overrides these implementations)
+ @Override
+ public float floatValue(float defaultValue) {
+ return _asFloatValueUnchecked();
+ }
+
+ @Override
+ public float asFloat() {
+ return _asFloatValueUnchecked();
+ }
+
+ @Override
+ public float asFloat(float defaultValue) {
+ return _asFloatValueUnchecked();
+ }
@Override
public double doubleValue() {
diff --git a/src/main/java/tools/jackson/databind/node/NumericNode.java b/src/main/java/tools/jackson/databind/node/NumericNode.java
index ef01df0c05..a187e1d188 100644
--- a/src/main/java/tools/jackson/databind/node/NumericNode.java
+++ b/src/main/java/tools/jackson/databind/node/NumericNode.java
@@ -42,6 +42,9 @@ protected final String _valueDesc() {
@Override public abstract Number numberValue();
@Override public abstract short shortValue();
+ @Override public abstract short shortValue(short defaultValue);
+ @Override public abstract short asShort();
+ @Override public abstract short asShort(short defaultValue);
@Override public abstract int intValue();
@Override public abstract int intValue(int defaultValue);
@@ -65,6 +68,9 @@ protected final String _valueDesc() {
@Override public abstract Optional asBigIntegerOpt();
@Override public abstract float floatValue();
+ @Override public abstract float floatValue(float defaultValue);
+ @Override public abstract float asFloat();
+ @Override public abstract float asFloat(float defaultValue);
@Override public abstract double doubleValue();
@Override public abstract double doubleValue(double defaultValue);
@@ -80,6 +86,7 @@ protected final String _valueDesc() {
@Override public abstract BigDecimal asDecimal(BigDecimal defaultValue);
@Override public abstract Optional asDecimalOpt();
+ @Override public abstract boolean canConvertToShort();
@Override public abstract boolean canConvertToInt();
@Override public abstract boolean canConvertToLong();
diff --git a/src/main/java/tools/jackson/databind/node/POJONode.java b/src/main/java/tools/jackson/databind/node/POJONode.java
index 64a512c3e6..6deebb9624 100644
--- a/src/main/java/tools/jackson/databind/node/POJONode.java
+++ b/src/main/java/tools/jackson/databind/node/POJONode.java
@@ -97,7 +97,21 @@ public byte[] binaryValue()
/* Overridden JsonNode methods, scalar access, numeric
/**********************************************************************
*/
-
+
+ // `shortValue()` (etc) fine as defaults (fail); but need to override `asShort()`
+
+ @Override
+ public short asShort() {
+ Short S = _extractAsShort();
+ return (S == null) ? super.asShort() : S;
+ }
+
+ @Override
+ public short asShort(short defaultValue) {
+ Short S = _extractAsShort();
+ return (S == null) ? defaultValue : S;
+ }
+
// `intValue()` (etc) fine as defaults (fail); but need to override `asInt()`
@Override
@@ -158,6 +172,22 @@ public Optional asBigIntegerOpt() {
return (big == null) ? Optional.empty() : Optional.of(big);
}
+ // `floatValue()` (etc) fine as defaults (fail); but need to override `asFloat()`
+
+ @Override
+ public float asFloat()
+ {
+ Float f = _extractAsFloat();
+ return (f == null) ? super.asFloat() : f;
+ }
+
+ @Override
+ public float asFloat(float defaultValue)
+ {
+ Float f = _extractAsFloat();
+ return (f == null) ? defaultValue : f;
+ }
+
// `doubleValue()` (etc) fine as defaults (fail); but need to override `asDouble()`
@Override
@@ -207,18 +237,19 @@ public Optional asDecimalOpt() {
}
// Consider only Integral numbers
- protected Integer _extractAsInteger() {
- // First, `null` same as `NullNode`
- if (_value == null) {
- return 0;
+ protected Short _extractAsShort() {
+ Long v = _extractAsLong();
+ if (v != null && v >= Short.MIN_VALUE && v <= Short.MAX_VALUE) {
+ return v.shortValue();
}
- // Next, coercions from integral Numbers
- if (_value instanceof Number N) {
- // !!! TODO: range checks
- if (N instanceof Long || N instanceof Integer || N instanceof Short || N instanceof Byte
- || N instanceof BigInteger) {
- return N.intValue();
- }
+ return null;
+ }
+
+ // Consider only Integral numbers
+ protected Integer _extractAsInteger() {
+ Long v = _extractAsLong();
+ if (v != null && v >= Integer.MIN_VALUE && v <= Integer.MAX_VALUE) {
+ return v.intValue();
}
return null;
}
@@ -231,9 +262,24 @@ protected Long _extractAsLong() {
}
// Next, coercions from integral Numbers
if (_value instanceof Number N) {
- // !!! TODO: range checks
- if (N instanceof Long || N instanceof Integer || N instanceof Short || N instanceof Byte
- || N instanceof BigInteger) {
+ // Add range check
+ if (N instanceof BigInteger big) {
+ if (big.compareTo(BI_MIN_LONG) >= 0 && big.compareTo(BI_MAX_LONG) <= 0) {
+ return big.longValue();
+ }
+ } else if (N instanceof BigDecimal dec) {
+ if (dec.compareTo(BD_MIN_LONG) >= 0 && dec.compareTo(BD_MAX_LONG) <= 0) {
+ return dec.longValue();
+ }
+ } else if (N instanceof Double D) {
+ if (D >= Long.MIN_VALUE && D <= Long.MAX_VALUE) {
+ return D.longValue();
+ }
+ } else if (N instanceof Float F) {
+ if (F >= Long.MIN_VALUE && F <= Long.MAX_VALUE) {
+ return F.longValue();
+ }
+ } else {
return N.longValue();
}
}
@@ -247,25 +293,40 @@ protected BigInteger _extractAsBigInteger() {
return BigInteger.ZERO;
}
// Next, coercions from Numbers
- if (_value instanceof BigInteger big) {
- return big;
- }
if (_value instanceof Number N) {
- if (N instanceof Long || N instanceof Integer || N instanceof Short || N instanceof Byte) {
+ if (_value instanceof BigInteger big) {
+ return big;
+ } else if (_value instanceof BigDecimal dec) {
+ return dec.toBigInteger();
+ } else {
return BigInteger.valueOf(N.longValue());
}
}
return null;
}
+ protected Float _extractAsFloat() {
+ if (_value instanceof Number N) {
+ if (_value instanceof Float F) {
+ return F;
+ }
+ float f = N.floatValue();
+ if (Float.isFinite(f)) {
+ return f;
+ }
+ }
+ return null;
+ }
+
protected Double _extractAsDouble() {
if (_value instanceof Number N) {
if (_value instanceof Double D) {
return D;
}
- // 24-Mar-2025, tatu: Should probably check for NaN from overflow
- // from "too big" `BigDecimal` or `BigInteger`. But will do for now
- return N.doubleValue();
+ double d = N.doubleValue();
+ if (Double.isFinite(d)) {
+ return d;
+ }
}
return null;
}
@@ -276,14 +337,12 @@ protected BigDecimal _extractAsBigDecimal() {
return BigDecimal.ZERO;
}
// Next, coercions from Numbers
- if (_value instanceof BigDecimal dec) {
- return dec;
- }
- if (_value instanceof BigInteger I) {
- return new BigDecimal(I);
- }
if (_value instanceof Number N) {
- if (N instanceof Long || N instanceof Integer || N instanceof Short || N instanceof Byte) {
+ if (_value instanceof BigDecimal dec) {
+ return dec;
+ } else if (_value instanceof BigInteger I) {
+ return new BigDecimal(I);
+ } else if (N instanceof Long || N instanceof Integer || N instanceof Short || N instanceof Byte) {
return BigDecimal.valueOf(N.longValue());
}
// Use doubleValue() as a last resort for Float & Double
diff --git a/src/main/java/tools/jackson/databind/node/ShortNode.java b/src/main/java/tools/jackson/databind/node/ShortNode.java
index 9569751b7b..c2b28d5786 100644
--- a/src/main/java/tools/jackson/databind/node/ShortNode.java
+++ b/src/main/java/tools/jackson/databind/node/ShortNode.java
@@ -69,6 +69,19 @@ public Number numberValue() {
@Override
public short shortValue() { return _value; }
+ @Override
+ public short shortValue(short defaultValue) { return _value; }
+
+ @Override
+ public short asShort() {
+ return _value;
+ }
+
+ @Override
+ public short asShort(short defaultValue) {
+ return _value;
+ }
+
@Override
public int intValue() { return _value; }
diff --git a/src/main/java/tools/jackson/databind/node/StringNode.java b/src/main/java/tools/jackson/databind/node/StringNode.java
index a0549d9ff9..6ac0929b79 100644
--- a/src/main/java/tools/jackson/databind/node/StringNode.java
+++ b/src/main/java/tools/jackson/databind/node/StringNode.java
@@ -163,6 +163,22 @@ public byte[] binaryValue() throws JacksonException {
/**********************************************************************
*/
+ @Override
+ public short asShort() {
+ Short S = _tryParseAsShort();
+ if (S == null) {
+ return _reportCoercionFail("asShort()", Short.TYPE,
+ "value not a valid String representation of `short`");
+ }
+ return S;
+ }
+
+ @Override
+ public short asShort(short defaultValue) {
+ Short S = _tryParseAsShort();
+ return (S == null) ? defaultValue : S;
+ }
+
@Override
public int asInt() {
Integer I = _tryParseAsInteger();
@@ -170,7 +186,7 @@ public int asInt() {
return _reportCoercionFail("asInt()", Integer.TYPE,
"value not a valid String representation of `int`");
}
- return I.intValue();
+ return I;
}
@Override
@@ -192,7 +208,7 @@ public long asLong() {
return _reportCoercionFail("asLong()", Long.TYPE,
"value not a valid String representation of `long`");
}
- return L.longValue();
+ return L;
}
@Override
@@ -231,6 +247,26 @@ public Optional asBigIntegerOpt() {
return (big == null) ? Optional.empty() : Optional.of(big);
}
+ // `floatValue()` (etc) fine as defaults (fail); but need to override `asFloat()`
+
+ @Override
+ public float asFloat()
+ {
+ Float F = _tryParseAsFloat();
+ if (F == null) {
+ return _reportCoercionFail("asFloat()", Float.TYPE,
+ "value not a valid String representation of `float`");
+ }
+ return F;
+ }
+
+ @Override
+ public float asFloat(float defaultValue)
+ {
+ Float F = _tryParseAsFloat();
+ return (F == null) ? defaultValue : F;
+ }
+
// `doubleValue()` (etc) fine as defaults (fail); but need to override `asDouble()`
@Override
@@ -241,7 +277,7 @@ public double asDouble()
return _reportCoercionFail("asDouble()", Double.TYPE,
"value not a valid String representation of `double`");
}
- return (d == null) ? super.asDouble() : d;
+ return d;
}
@Override
@@ -281,6 +317,14 @@ public Optional asDecimalOpt() {
return (dec == null) ? Optional.empty() : Optional.of(dec);
}
+ protected Short _tryParseAsShort() {
+ Integer I = _tryParseAsInteger();
+ if (I != null && I >= Short.MIN_VALUE && I <= Short.MAX_VALUE) {
+ return I.shortValue();
+ }
+ return null;
+ }
+
protected Integer _tryParseAsInteger() {
if (NumberInput.looksLikeValidNumber(_value)) {
try {
@@ -315,6 +359,17 @@ protected BigInteger _tryParseAsBigInteger() {
return null;
}
+ protected Float _tryParseAsFloat() {
+ if (NumberInput.looksLikeValidNumber(_value)) {
+ try {
+ return NumberInput.parseFloat(_value, true);
+ } catch (NumberFormatException e) {
+ ;
+ }
+ }
+ return null;
+ }
+
protected Double _tryParseAsDouble() {
if (NumberInput.looksLikeValidNumber(_value)) {
try {
diff --git a/src/main/java/tools/jackson/databind/node/ValueNode.java b/src/main/java/tools/jackson/databind/node/ValueNode.java
index 336381d366..4e30396ac4 100644
--- a/src/main/java/tools/jackson/databind/node/ValueNode.java
+++ b/src/main/java/tools/jackson/databind/node/ValueNode.java
@@ -1,5 +1,7 @@
package tools.jackson.databind.node;
+import java.math.BigDecimal;
+import java.math.BigInteger;
import java.util.List;
import tools.jackson.core.*;
@@ -18,6 +20,21 @@ public abstract class ValueNode
{
private static final long serialVersionUID = 3L;
+ // For numeric range checks
+ protected final static BigInteger BI_MIN_SHORT = BigInteger.valueOf(Short.MIN_VALUE);
+ protected final static BigInteger BI_MAX_SHORT = BigInteger.valueOf(Short.MAX_VALUE);
+ protected final static BigInteger BI_MIN_INTEGER = BigInteger.valueOf(Integer.MIN_VALUE);
+ protected final static BigInteger BI_MAX_INTEGER = BigInteger.valueOf(Integer.MAX_VALUE);
+ protected final static BigInteger BI_MIN_LONG = BigInteger.valueOf(Long.MIN_VALUE);
+ protected final static BigInteger BI_MAX_LONG = BigInteger.valueOf(Long.MAX_VALUE);
+
+ protected final static BigDecimal BD_MIN_SHORT = BigDecimal.valueOf(Short.MIN_VALUE);
+ protected final static BigDecimal BD_MAX_SHORT = BigDecimal.valueOf(Short.MAX_VALUE);
+ protected final static BigDecimal BD_MIN_INTEGER = BigDecimal.valueOf(Integer.MIN_VALUE);
+ protected final static BigDecimal BD_MAX_INTEGER = BigDecimal.valueOf(Integer.MAX_VALUE);
+ protected final static BigDecimal BD_MIN_LONG = BigDecimal.valueOf(Long.MIN_VALUE);
+ protected final static BigDecimal BD_MAX_LONG = BigDecimal.valueOf(Long.MAX_VALUE);
+
protected final static JsonNode MISSING = MissingNode.getInstance();
protected ValueNode() { }
diff --git a/src/test/java/tools/jackson/databind/node/JsonNodeDoubleValueTest.java b/src/test/java/tools/jackson/databind/node/JsonNodeDoubleValueTest.java
index b50f630efa..b7be1bcfe4 100644
--- a/src/test/java/tools/jackson/databind/node/JsonNodeDoubleValueTest.java
+++ b/src/test/java/tools/jackson/databind/node/JsonNodeDoubleValueTest.java
@@ -108,6 +108,7 @@ public void failDoubleValueFromNonNumberScalar()
_assertDoubleValueFailForNonNumber(NODES.stringNode("123"));
_assertDoubleValueFailForNonNumber(NODES.rawValueNode(new RawValue("abc")));
_assertDoubleValueFailForNonNumber(NODES.pojoNode(Boolean.TRUE));
+ _assertDoubleValueFailForNonNumber(NODES.pojoNode(3.8d));
}
@Test
@@ -213,10 +214,12 @@ public void asDoubleFromNonNumberScalar()
_assertAsDoubleFailForNonNumber(NODES.pojoNode(Boolean.TRUE));
_assertAsDoubleFailForNonNumber(NODES.stringNode("abc"),
"not a valid String representation of `double`");
+ _assertAsDoubleFailForNonNumber(NODES.pojoNode(new BigDecimal(BigInteger.TEN.pow(310))));
// Then passing ones:
- _assertAsDouble(2.5d, NODES.pojoNode(2.5d));
_assertAsDouble(0.5d, NODES.stringNode("0.5"));
+ _assertAsDouble(2.5d, NODES.pojoNode(2.5d));
+ _assertAsDouble(1e40, NODES.pojoNode(1e40));
}
@Test
diff --git a/src/test/java/tools/jackson/databind/node/JsonNodeFloatValueTest.java b/src/test/java/tools/jackson/databind/node/JsonNodeFloatValueTest.java
index 3be255f7e2..1f12a5dfc5 100644
--- a/src/test/java/tools/jackson/databind/node/JsonNodeFloatValueTest.java
+++ b/src/test/java/tools/jackson/databind/node/JsonNodeFloatValueTest.java
@@ -2,6 +2,7 @@
import java.math.BigDecimal;
import java.math.BigInteger;
+import java.util.OptionalDouble;
import org.junit.jupiter.api.Test;
@@ -26,28 +27,28 @@ public class JsonNodeFloatValueTest
@Test
public void floatValueFromNumberIntOk()
{
- final float ONE_D = 1.0f;
+ final float ONE_F = 1.0f;
// Then other integer types
- assertEquals(ONE_D, NODES.numberNode((byte) 1).floatValue());
- assertEquals((float)Byte.MIN_VALUE, NODES.numberNode(Byte.MIN_VALUE).floatValue());
- assertEquals((float)Byte.MAX_VALUE, NODES.numberNode(Byte.MAX_VALUE).floatValue());
+ _assertFloatValue(ONE_F, NODES.numberNode((byte) 1));
+ _assertFloatValue((float)Byte.MIN_VALUE, NODES.numberNode(Byte.MIN_VALUE));
+ _assertFloatValue((float)Byte.MAX_VALUE, NODES.numberNode(Byte.MAX_VALUE));
- assertEquals(ONE_D, NODES.numberNode((short) 1).floatValue());
- assertEquals((float)Short.MIN_VALUE, NODES.numberNode(Short.MIN_VALUE).floatValue());
- assertEquals((float)Short.MAX_VALUE, NODES.numberNode(Short.MAX_VALUE).floatValue());
+ _assertFloatValue(ONE_F, NODES.numberNode((short) 1));
+ _assertFloatValue((float)Short.MIN_VALUE, NODES.numberNode(Short.MIN_VALUE));
+ _assertFloatValue((float)Short.MAX_VALUE, NODES.numberNode(Short.MAX_VALUE));
- assertEquals(ONE_D, NODES.numberNode(1).floatValue());
- assertEquals((float) Integer.MIN_VALUE, NODES.numberNode(Integer.MIN_VALUE).floatValue());
- assertEquals((float) Integer.MAX_VALUE, NODES.numberNode(Integer.MAX_VALUE).floatValue());
-
- assertEquals(ONE_D, NODES.numberNode(1L).floatValue());
- assertEquals((float) Long.MIN_VALUE, NODES.numberNode(Long.MIN_VALUE).floatValue());
- assertEquals((float) Long.MAX_VALUE, NODES.numberNode(Long.MAX_VALUE).floatValue());
+ _assertFloatValue(ONE_F, NODES.numberNode(1));
+ _assertFloatValue((float) Integer.MIN_VALUE, NODES.numberNode(Integer.MIN_VALUE));
+ _assertFloatValue((float) Integer.MAX_VALUE, NODES.numberNode(Integer.MAX_VALUE));
+
+ _assertFloatValue(ONE_F, NODES.numberNode(1L));
+ _assertFloatValue((float) Long.MIN_VALUE, NODES.numberNode(Long.MIN_VALUE));
+ _assertFloatValue((float) Long.MAX_VALUE, NODES.numberNode(Long.MAX_VALUE));
- assertEquals(ONE_D, NODES.numberNode(BigInteger.valueOf(1)).floatValue());
- assertEquals((float) Long.MIN_VALUE, NODES.numberNode(BigInteger.valueOf(Long.MIN_VALUE)).floatValue());
- assertEquals((float) Long.MAX_VALUE, NODES.numberNode(BigInteger.valueOf(Long.MAX_VALUE)).floatValue());
+ _assertFloatValue(ONE_F, NODES.numberNode(BigInteger.valueOf(1)));
+ _assertFloatValue((float) Long.MIN_VALUE, NODES.numberNode(BigInteger.valueOf(Long.MIN_VALUE)));
+ _assertFloatValue((float) Long.MAX_VALUE, NODES.numberNode(BigInteger.valueOf(Long.MAX_VALUE)));
}
@Test
@@ -67,20 +68,20 @@ public void failfloatValueFromNumberIntRange() {
@Test
public void floatValueFromNumberFPOk()
{
- assertEquals(1.0f, NODES.numberNode(1.0f).floatValue());
- assertEquals(100_000.25f, NODES.numberNode(100_000.25f).floatValue());
- assertEquals(-100_000.25f, NODES.numberNode(-100_000.25f).floatValue());
+ _assertFloatValue(1.0f, NODES.numberNode(1.0f));
+ _assertFloatValue(100_000.25f, NODES.numberNode(100_000.25f));
+ _assertFloatValue(-100_000.25f, NODES.numberNode(-100_000.25f));
- assertEquals(1.0f, NODES.numberNode(1.0d).floatValue());
- assertEquals(100_000.25f, NODES.numberNode(100_000.25d).floatValue());
- assertEquals(-100_000.25f, NODES.numberNode(-100_000.25d).floatValue());
+ _assertFloatValue(1.0f, NODES.numberNode(1.0d));
+ _assertFloatValue(100_000.25f, NODES.numberNode(100_000.25d));
+ _assertFloatValue(-100_000.25f, NODES.numberNode(-100_000.25d));
- assertEquals(1.25f,
- NODES.numberNode(BigDecimal.valueOf(1.25d)).floatValue());
- assertEquals((float) Long.MIN_VALUE,
- NODES.numberNode(BigDecimal.valueOf((double) Long.MIN_VALUE)).floatValue());
- assertEquals((float) Long.MAX_VALUE,
- NODES.numberNode(BigDecimal.valueOf((double) Long.MAX_VALUE)).floatValue());
+ _assertFloatValue(1.25f,
+ NODES.numberNode(BigDecimal.valueOf(1.25d)));
+ _assertFloatValue((float) Long.MIN_VALUE,
+ NODES.numberNode(BigDecimal.valueOf((double) Long.MIN_VALUE)));
+ _assertFloatValue((float) Long.MAX_VALUE,
+ NODES.numberNode(BigDecimal.valueOf((double) Long.MAX_VALUE)));
}
@Test
@@ -111,6 +112,7 @@ public void failFloatValueFromNonNumberScalar()
_assertFailFloatForNonNumber(NODES.stringNode("123"));
_assertFailFloatForNonNumber(NODES.rawValueNode(new RawValue("abc")));
_assertFailFloatForNonNumber(NODES.pojoNode(Boolean.TRUE));
+ _assertFailFloatForNonNumber(NODES.pojoNode(3.8f));
}
@Test
@@ -127,8 +129,127 @@ public void failFloatValueFromMiscOther()
_assertFailFloatForNonNumber(NODES.missingNode());
}
+ // // // asFloat()
+
+ // from Integers
+
+ @Test
+ public void asFloatFromNumberIntOk()
+ {
+ final float ONE_F = (float) 1;
+
+ _assertAsFloat(ONE_F, NODES.numberNode((byte) 1));
+ _assertAsFloat((float)Byte.MIN_VALUE, NODES.numberNode(Byte.MIN_VALUE));
+ _assertAsFloat((float)Byte.MAX_VALUE, NODES.numberNode(Byte.MAX_VALUE));
+
+ _assertAsFloat(ONE_F, NODES.numberNode((short) 1));
+ _assertAsFloat((float)Short.MIN_VALUE, NODES.numberNode(Short.MIN_VALUE));
+ _assertAsFloat((float)Short.MAX_VALUE, NODES.numberNode(Short.MAX_VALUE));
+
+ _assertAsFloat(ONE_F, NODES.numberNode(1));
+ _assertAsFloat((float) Integer.MIN_VALUE, NODES.numberNode(Integer.MIN_VALUE));
+ _assertAsFloat((float) Integer.MAX_VALUE, NODES.numberNode(Integer.MAX_VALUE));
+
+ _assertAsFloat(ONE_F, NODES.numberNode(1L));
+ _assertAsFloat((float) Long.MIN_VALUE, NODES.numberNode(Long.MIN_VALUE));
+ _assertAsFloat((float) Long.MAX_VALUE, NODES.numberNode(Long.MAX_VALUE));
+
+ _assertAsFloat(ONE_F, NODES.numberNode(BigInteger.valueOf(1)));
+ _assertAsFloat((float) Long.MIN_VALUE, NODES.numberNode(BigInteger.valueOf(Long.MIN_VALUE)));
+ _assertAsFloat((float) Long.MAX_VALUE, NODES.numberNode(BigInteger.valueOf(Long.MAX_VALUE)));
+ }
+
+ @Test
+ public void asFloatFailFromNumberIntRange() {
+ // Can only fail for underflow/overflow: and that only for / BigInteger
+ // (neither Integer nor Long is outside of range of even Float).
+
+ final BigInteger tooBig = BigInteger.TEN.pow(310);
+ final BigInteger tooSmall = tooBig.negate();
+
+ _assertAsFloatFailForValueRange(NODES.numberNode(tooBig));
+ _assertAsFloatFailForValueRange(NODES.numberNode(tooSmall));
+ }
+
+ // Numbers/FPs
+
+ @Test
+ public void asFloatFromNumberFPOk()
+ {
+ _assertAsFloat(1.0f, NODES.numberNode(1.0f));
+ _assertAsFloat(100_000.0f, NODES.numberNode(100_000.0f));
+ _assertAsFloat(-100_000.0f, NODES.numberNode(-100_000.0f));
+
+ _assertAsFloat(1.0f, NODES.numberNode(1.0d));
+ _assertAsFloat(100_000.0f, NODES.numberNode(100_000.0d));
+ _assertAsFloat(-100_000.0f, NODES.numberNode(-100_000.0d));
+
+ _assertAsFloat(1.0f,
+ NODES.numberNode(BigDecimal.valueOf(1.0d)));
+ _assertAsFloat((float) Long.MIN_VALUE,
+ NODES.numberNode(BigDecimal.valueOf((float) Long.MIN_VALUE)));
+ _assertAsFloat((float) Long.MAX_VALUE,
+ NODES.numberNode(BigDecimal.valueOf((float) Long.MAX_VALUE)));
+ }
+
+ @Test
+ public void asFloatFromNumberFPRangeFail()
+ {
+ // Can only fail from BigDecimal (similar to ints vs BigInteger)
+
+ final BigDecimal tooBig = new BigDecimal(BigInteger.TEN.pow(310))
+ .add(BigDecimal.valueOf(0.125));
+ final BigDecimal tooSmall = tooBig.negate();
+
+ _assertAsFloatFailForValueRange(NODES.numberNode(tooBig));
+ _assertAsFloatFailForValueRange(NODES.numberNode(tooSmall));
+ }
+
+ // from non-Numeric types
+
+ @Test
+ public void asFloatFromNonNumberScalar()
+ {
+ // First, failing cases:
+
+ _assertAsFloatFailForNonNumber(NODES.booleanNode(true));
+ _assertAsFloatFailForNonNumber(NODES.binaryNode(new byte[3]));
+ _assertAsFloatFailForNonNumber(NODES.rawValueNode(new RawValue("abc")));
+ _assertAsFloatFailForNonNumber(NODES.pojoNode(Boolean.TRUE));
+ _assertAsFloatFailForNonNumber(NODES.stringNode("abc"),
+ "not a valid String representation of `float`");
+ _assertAsFloatFailForNonNumber(NODES.pojoNode(1e40));
+
+ // Then passing ones:
+ _assertAsFloat(2.5f, NODES.pojoNode(2.5f));
+ _assertAsFloat(0.5f, NODES.stringNode("0.5"));
+ }
+
+ @Test
+ public void asFloatFromStructuralFail()
+ {
+ _assertAsFloatFailForNonNumber(NODES.arrayNode(3));
+ _assertAsFloatFailForNonNumber(NODES.objectNode());
+ }
+
+ @Test
+ public void asFloatFromMiscOther()
+ {
+ // Null node converts to 0.0f; missing fails
+ _assertAsFloat((float) 0, NODES.nullNode());
+ _assertAsFloatFailForNonNumber(NODES.missingNode());
+ }
+
+
// // // Shared helper methods
+ private void _assertFloatValue(float expected, JsonNode node) {
+ assertEquals(expected, node.floatValue());
+
+ // and defaults
+ assertEquals(expected, node.floatValue(-9999.5f));
+ }
+
private void _assertFailFloatForValueRange(JsonNode node) {
Exception e = assertThrows(JsonNodeException.class,
() -> node.floatValue(),
@@ -136,6 +257,8 @@ private void _assertFailFloatForValueRange(JsonNode node) {
assertThat(e.getMessage())
.contains("cannot convert value")
.contains("value not in 32-bit `float` range");
+
+ assertEquals(-2.25f, node.floatValue(-2.25f));
}
private void _assertFailFloatForNonNumber(JsonNode node) {
@@ -145,5 +268,43 @@ private void _assertFailFloatForNonNumber(JsonNode node) {
assertThat(e.getMessage())
.contains("cannot convert value")
.contains("value type not numeric");
+
+ assertEquals(-2.25f, node.floatValue(-2.25f));
}
+
+ private void _assertAsFloat(float expected, JsonNode node) {
+ assertEquals(expected, node.asFloat());
+
+ // and defaults
+ assertEquals(expected, node.asFloat(-9999.5f));
+ }
+
+ private void _assertAsFloatFailForValueRange(JsonNode node) {
+ Exception e = assertThrows(JsonNodeException.class,
+ () -> node.asFloat(),
+ "For ("+node.getClass().getSimpleName()+") value: "+node);
+ assertThat(e.getMessage())
+ .contains("asFloat()")
+ .contains("cannot convert value")
+ .contains("value not in 32-bit `float` range");
+
+ assertEquals(-2.25f, node.asFloat(-2.25f));
+ }
+
+ private void _assertAsFloatFailForNonNumber(JsonNode node) {
+ _assertAsFloatFailForNonNumber(node, "value type not numeric");
+ }
+
+ private void _assertAsFloatFailForNonNumber(JsonNode node, String extraMatch) {
+ Exception e = assertThrows(JsonNodeException.class,
+ () -> node.asFloat(),
+ "For ("+node.getClass().getSimpleName()+") value: "+node);
+ assertThat(e.getMessage())
+ .contains("asFloat()")
+ .contains("cannot convert value")
+ .contains(extraMatch);
+
+ assertEquals(1.5f, node.asFloat(1.5f));
+ }
+
}
diff --git a/src/test/java/tools/jackson/databind/node/JsonNodeIntValueTest.java b/src/test/java/tools/jackson/databind/node/JsonNodeIntValueTest.java
index 70de8afa4e..3d30e11dec 100644
--- a/src/test/java/tools/jackson/databind/node/JsonNodeIntValueTest.java
+++ b/src/test/java/tools/jackson/databind/node/JsonNodeIntValueTest.java
@@ -143,6 +143,7 @@ public void intValueFromNonNumberScalarFail()
_assertIntValueFailForNonNumber(NODES.stringNode("123"));
_assertIntValueFailForNonNumber(NODES.rawValueNode(new RawValue("abc")));
_assertIntValueFailForNonNumber(NODES.pojoNode(Boolean.TRUE));
+ _assertIntValueFailForNonNumber(NODES.pojoNode(456));
}
@Test
@@ -279,14 +280,14 @@ public void asIntFromNonNumberScalar()
_assertAsIntFailForNonNumber(NODES.binaryNode(new byte[3]));
_assertAsIntFailForNonNumber(NODES.rawValueNode(new RawValue("abc")));
_assertAsIntFailForNonNumber(NODES.pojoNode(Boolean.TRUE));
-
_assertAsIntFailForNonNumber(NODES.stringNode("abc"),
"value not a valid String representation of `int`");
+ _assertAsIntFailForNonNumber(NODES.pojoNode(123_456_789_000L));
// Some pass:
-
- _assertAsInt(456, NODES.pojoNode(456));
_assertAsInt(123, NODES.stringNode("123"));
+ _assertAsInt(456, NODES.pojoNode(456L));
+ _assertAsInt(789, NODES.pojoNode(BigInteger.valueOf(789)));
}
@Test
diff --git a/src/test/java/tools/jackson/databind/node/JsonNodeLongValueTest.java b/src/test/java/tools/jackson/databind/node/JsonNodeLongValueTest.java
index 0ceb308497..3df60fe1f0 100644
--- a/src/test/java/tools/jackson/databind/node/JsonNodeLongValueTest.java
+++ b/src/test/java/tools/jackson/databind/node/JsonNodeLongValueTest.java
@@ -140,6 +140,7 @@ public void longValueFromNonNumberScalarFail()
_assertLongValueFailForNonNumber(NODES.stringNode("123"));
_assertLongValueFailForNonNumber(NODES.rawValueNode(new RawValue("abc")));
_assertLongValueFailForNonNumber(NODES.pojoNode(Boolean.TRUE));
+ _assertLongValueFailForNonNumber(NODES.pojoNode(456L));
}
@Test
@@ -276,10 +277,12 @@ public void asLongFromNonNumberScalarFail()
_assertAsLongFailForNonNumber(NODES.rawValueNode(new RawValue("abc")));
_assertAsLongFailForNonNumber(NODES.pojoNode(Boolean.TRUE));
_assertAsLongFailForNonNumber(NODES.stringNode("abcdef"), "not a valid String representation of `long`");
+ _assertAsLongFailForNonNumber(NODES.pojoNode(1e40));
// Some pass
- _assertAsLong(123456L, NODES.pojoNode(123456L));
_assertAsLong(1234L, NODES.stringNode("1234"));
+ _assertAsLong(123456L, NODES.pojoNode(123456L));
+ _assertAsLong(789L, NODES.pojoNode(BigInteger.valueOf(789)));
}
@Test
diff --git a/src/test/java/tools/jackson/databind/node/JsonNodeShortValueTest.java b/src/test/java/tools/jackson/databind/node/JsonNodeShortValueTest.java
index 93ac0fc4be..816be439ab 100644
--- a/src/test/java/tools/jackson/databind/node/JsonNodeShortValueTest.java
+++ b/src/test/java/tools/jackson/databind/node/JsonNodeShortValueTest.java
@@ -34,27 +34,27 @@ public void shortValueFromNumberIntOk()
final short MAX_SHORT = Short.MAX_VALUE;
// First safe from `short`
- assertEquals(SHORT_1, NODES.numberNode((short) 1).shortValue());
- assertEquals((int)Short.MIN_VALUE, NODES.numberNode(MIN_SHORT).shortValue());
- assertEquals((int)Short.MAX_VALUE, NODES.numberNode(MAX_SHORT).shortValue());
+ _assertShortValue(SHORT_1, NODES.numberNode((short) 1));
+ _assertShortValue(Short.MIN_VALUE, NODES.numberNode(MIN_SHORT));
+ _assertShortValue(Short.MAX_VALUE, NODES.numberNode(MAX_SHORT));
// Then other integer types
- assertEquals(SHORT_1, NODES.numberNode((byte) 1).shortValue());
- assertEquals((short) Byte.MIN_VALUE, NODES.numberNode(Byte.MIN_VALUE).shortValue());
- assertEquals((short) Byte.MAX_VALUE, NODES.numberNode(Byte.MAX_VALUE).shortValue());
+ _assertShortValue(SHORT_1, NODES.numberNode((byte) 1));
+ _assertShortValue(Byte.MIN_VALUE, NODES.numberNode(Byte.MIN_VALUE));
+ _assertShortValue(Byte.MAX_VALUE, NODES.numberNode(Byte.MAX_VALUE));
- assertEquals(SHORT_1, NODES.numberNode(1).shortValue());
- assertEquals(MIN_SHORT, NODES.numberNode((int) MIN_SHORT).shortValue());
- assertEquals(MAX_SHORT, NODES.numberNode((int) MAX_SHORT).shortValue());
+ _assertShortValue(SHORT_1, NODES.numberNode(1));
+ _assertShortValue(MIN_SHORT, NODES.numberNode((int) MIN_SHORT));
+ _assertShortValue(MAX_SHORT, NODES.numberNode((int) MAX_SHORT));
- assertEquals(SHORT_1, NODES.numberNode(1L).shortValue());
- assertEquals(MIN_SHORT, NODES.numberNode((long) MIN_SHORT).shortValue());
- assertEquals(MAX_SHORT, NODES.numberNode((long) MAX_SHORT).shortValue());
+ _assertShortValue(SHORT_1, NODES.numberNode(1L));
+ _assertShortValue(MIN_SHORT, NODES.numberNode((long) MIN_SHORT));
+ _assertShortValue(MAX_SHORT, NODES.numberNode((long) MAX_SHORT));
- assertEquals(SHORT_1, NODES.numberNode(BigInteger.valueOf(1)).shortValue());
- assertEquals(MIN_SHORT, NODES.numberNode(BigInteger.valueOf(MIN_SHORT)).shortValue());
- assertEquals(MAX_SHORT, NODES.numberNode(BigInteger.valueOf(MAX_SHORT)).shortValue());
+ _assertShortValue(SHORT_1, NODES.numberNode(BigInteger.valueOf(1)));
+ _assertShortValue(MIN_SHORT, NODES.numberNode(BigInteger.valueOf(MIN_SHORT)));
+ _assertShortValue(MAX_SHORT, NODES.numberNode(BigInteger.valueOf(MAX_SHORT)));
}
@Test
@@ -73,22 +73,22 @@ public void shortValueFromNumberIntFailRange() {
@Test
public void shortValueFromNumberFPOk()
{
- assertEquals(1, NODES.numberNode(1.0f).shortValue());
- assertEquals(10_000, NODES.numberNode(10_000.0f).shortValue());
- assertEquals(-10_000, NODES.numberNode(-10_000.0f).shortValue());
-
- assertEquals(1, NODES.numberNode(1.0d).shortValue());
- assertEquals(10_000, NODES.numberNode(10_000.0d).shortValue());
- assertEquals(-10_000, NODES.numberNode(-10_000.0d).shortValue());
- assertEquals(Short.MIN_VALUE, NODES.numberNode((double) Short.MIN_VALUE).shortValue());
- assertEquals(Short.MAX_VALUE, NODES.numberNode((double) Short.MAX_VALUE).shortValue());
-
- assertEquals(1,
- NODES.numberNode(BigDecimal.valueOf(1.0d)).shortValue());
- assertEquals(Short.MIN_VALUE,
- NODES.numberNode(BigDecimal.valueOf((double) Short.MIN_VALUE)).shortValue());
- assertEquals(Short.MAX_VALUE,
- NODES.numberNode(BigDecimal.valueOf((double) Short.MAX_VALUE)).shortValue());
+ _assertShortValue((short) 1, NODES.numberNode(1.0f));
+ _assertShortValue((short) 10_000, NODES.numberNode(10_000.0f));
+ _assertShortValue((short) -10_000, NODES.numberNode(-10_000.0f));
+
+ _assertShortValue((short) 1, NODES.numberNode(1.0d));
+ _assertShortValue((short) 10_000, NODES.numberNode(10_000.0d));
+ _assertShortValue((short) -10_000, NODES.numberNode(-10_000.0d));
+ _assertShortValue(Short.MIN_VALUE, NODES.numberNode((double) Short.MIN_VALUE));
+ _assertShortValue(Short.MAX_VALUE, NODES.numberNode((double) Short.MAX_VALUE));
+
+ _assertShortValue((short) 1,
+ NODES.numberNode(BigDecimal.valueOf(1.0d)));
+ _assertShortValue(Short.MIN_VALUE,
+ NODES.numberNode(BigDecimal.valueOf((double) Short.MIN_VALUE)));
+ _assertShortValue(Short.MAX_VALUE,
+ NODES.numberNode(BigDecimal.valueOf((double) Short.MAX_VALUE)));
}
@Test
@@ -137,6 +137,163 @@ public void shortValueFromNonNumberFail()
_assertFailShortForNonNumber(NODES.nullNode());
_assertFailShortForNonNumber(NODES.missingNode());
+ _assertFailShortForNonNumber(NODES.pojoNode((short) 456));
+ }
+
+ // // // asShort()
+
+ // Numbers/Integers
+
+ @Test
+ public void asShortFromNumberIntOk()
+ {
+ // First safe from `short`
+ _assertAsShort((short) 1, NODES.numberNode((short) 1));
+ _assertAsShort(Short.MIN_VALUE, NODES.numberNode(Short.MIN_VALUE));
+ _assertAsShort(Short.MAX_VALUE, NODES.numberNode(Short.MAX_VALUE));
+
+ // Then other integer types
+ _assertAsShort((short) 1, NODES.numberNode((byte) 1));
+ _assertAsShort(Byte.MIN_VALUE, NODES.numberNode(Byte.MIN_VALUE));
+ _assertAsShort(Byte.MAX_VALUE, NODES.numberNode(Byte.MAX_VALUE));
+
+ _assertAsShort((short) 1, NODES.numberNode(1));
+ _assertAsShort(Short.MIN_VALUE, NODES.numberNode((int) Short.MIN_VALUE));
+ _assertAsShort(Short.MAX_VALUE, NODES.numberNode((int) Short.MAX_VALUE));
+
+ _assertAsShort((short) 1, NODES.numberNode(1L));
+ _assertAsShort(Short.MIN_VALUE, NODES.numberNode((long) Short.MIN_VALUE));
+ _assertAsShort(Short.MAX_VALUE, NODES.numberNode((long) Short.MAX_VALUE));
+
+ _assertAsShort((short) 1, NODES.numberNode(BigInteger.valueOf(1)));
+ _assertAsShort(Short.MIN_VALUE, NODES.numberNode(BigInteger.valueOf(Short.MIN_VALUE)));
+ _assertAsShort(Short.MAX_VALUE, NODES.numberNode(BigInteger.valueOf(Short.MAX_VALUE)));
+ }
+
+ @Test
+ public void asShortFromNumberIntFailRange() {
+ // Can only fail for underflow/overflow: and that only for Integer / Long / BigInteger
+ final int underflow = -1 + Short.MIN_VALUE;
+ final long overflow = +1L + Short.MAX_VALUE;
+
+ _assertAsShortFailForValueRange(NODES.numberNode(underflow));
+ _assertAsShortFailForValueRange(NODES.numberNode(overflow));
+
+ _assertAsShortFailForValueRange(NODES.numberNode(BigInteger.valueOf(underflow)));
+ _assertAsShortFailForValueRange(NODES.numberNode(BigInteger.valueOf(overflow)));
+ }
+
+ // Numbers/FPs
+
+ @Test
+ public void asShortFromNumberFPOk()
+ {
+ _assertAsShort((short) 1, NODES.numberNode(1.0f));
+ _assertAsShort((short) 100, NODES.numberNode(100.0f));
+ _assertAsShort((short) -100, NODES.numberNode(-100.0f));
+
+ _assertAsShort((short) 1, NODES.numberNode(1.0d));
+ _assertAsShort((short) 100, NODES.numberNode(100.0d));
+ _assertAsShort((short) -100, NODES.numberNode(-100.0d));
+ _assertAsShort(Short.MIN_VALUE, NODES.numberNode((double) Short.MIN_VALUE));
+ _assertAsShort(Short.MAX_VALUE, NODES.numberNode((double) Short.MAX_VALUE));
+
+ _assertAsShort((short) 1,
+ NODES.numberNode(BigDecimal.valueOf(1.0d)));
+ _assertAsShort(Short.MIN_VALUE,
+ NODES.numberNode(BigDecimal.valueOf((double) Short.MIN_VALUE)));
+ _assertAsShort(Short.MAX_VALUE,
+ NODES.numberNode(BigDecimal.valueOf((double) Short.MAX_VALUE)));
+ }
+
+ @Test
+ public void asShortFromNumberFPFailRange()
+ {
+ // Can only fail for underflow/overflow: and that only for Long / BigInteger
+ final long underflow = Short.MIN_VALUE - 1L;
+ final long overflow = Short.MAX_VALUE + 1L;
+
+ _assertAsShortFailForValueRange(NODES.numberNode((float)underflow));
+ _assertAsShortFailForValueRange(NODES.numberNode((double)overflow));
+
+ // Float is too inexact for using same test as Double, so:
+
+ _assertAsShortFailForValueRange(NODES.numberNode(-Float.MAX_VALUE));
+ _assertAsShortFailForValueRange(NODES.numberNode(Float.MAX_VALUE));
+
+ _assertAsShortFailForValueRange(NODES.numberNode(BigDecimal.valueOf(underflow)));
+ _assertAsShortFailForValueRange(NODES.numberNode(BigDecimal.valueOf(overflow)));
+ }
+
+ @Test
+ public void asShortFromNumberFPWithFraction()
+ {
+ _assertAsShort((short) 100, NODES.numberNode(100.75f));
+ _assertAsShort((short) -1, NODES.numberNode(-1.25f));
+
+ _assertAsShort((short) 100, NODES.numberNode(100.75d));
+ _assertAsShort((short) -1, NODES.numberNode(-1.25d));
+
+ _assertAsShort((short) 100, NODES.numberNode(BigDecimal.valueOf(100.75d)));
+ _assertAsShort((short) -1, NODES.numberNode(BigDecimal.valueOf(-1.25d)));
+ }
+
+ @Test
+ public void asIntFromNumberFPFailNaN()
+ {
+ _assertAsShortFailForNaN(NODES.numberNode(Float.NaN));
+ _assertAsShortFailForNaN(NODES.numberNode(Float.NEGATIVE_INFINITY));
+ _assertAsShortFailForNaN(NODES.numberNode(Float.POSITIVE_INFINITY));
+
+ _assertAsShortFailForNaN(NODES.numberNode(Double.NaN));
+ _assertAsShortFailForNaN(NODES.numberNode(Double.NEGATIVE_INFINITY));
+ _assertAsShortFailForNaN(NODES.numberNode(Double.POSITIVE_INFINITY));
+ }
+
+ // non-Numeric types
+
+ @Test
+ public void asShortFromNonNumberScalar()
+ {
+ // Some fail:
+ _assertAsShortFailForNonNumber(NODES.booleanNode(true));
+ _assertAsShortFailForNonNumber(NODES.binaryNode(new byte[3]));
+ _assertAsShortFailForNonNumber(NODES.rawValueNode(new RawValue("abc")));
+ _assertAsShortFailForNonNumber(NODES.pojoNode(Boolean.TRUE));
+ _assertAsShortFailForNonNumber(NODES.stringNode("abc"),
+ "value not a valid String representation of `short`");
+ _assertAsShortFailForNonNumber(NODES.pojoNode(123_456));
+
+ // Some pass:
+ _assertAsShort((short) 123, NODES.stringNode("123"));
+ _assertAsShort((short) 456, NODES.pojoNode(456));
+ _assertAsShort((short) 789, NODES.pojoNode(BigInteger.valueOf(789)));
+ }
+
+ @Test
+ public void asIntFromStructuralFail()
+ {
+ _assertAsShortFailForNonNumber(NODES.arrayNode(3));
+ _assertAsShortFailForNonNumber(NODES.objectNode());
+ }
+
+ @Test
+ public void asIntFromMiscOther()
+ {
+ // NullNode -> 0 but "missing" still fails
+ _assertAsShort((short) 0, NODES.nullNode());
+
+ _assertAsShortFailForNonNumber(NODES.missingNode());
+ }
+
+
+ // // // Shared helper methods: shortValue()
+
+ private void _assertShortValue(short expected, JsonNode node) {
+ assertEquals(expected, node.shortValue());
+
+ // and defaulting
+ assertEquals(expected, node.shortValue((short) 99));
}
// // // Shared helper methods
@@ -149,6 +306,8 @@ private void _assertFailShortForValueRange(JsonNode node) {
.contains("cannot convert value")
.contains("value not in 16-bit `short` range");
+ // assert defaulting
+ assertEquals(99, node.shortValue((short) 99));
}
private void _assertFailShortValueForFraction(JsonNode node) {
@@ -158,6 +317,9 @@ private void _assertFailShortValueForFraction(JsonNode node) {
assertThat(e.getMessage())
.contains("cannot convert value")
.contains("to `short`: value has fractional part");
+
+ // assert defaulting
+ assertEquals(99, node.shortValue((short) 99));
}
private void _assertFailShortForNonNumber(JsonNode node) {
@@ -167,5 +329,62 @@ private void _assertFailShortForNonNumber(JsonNode node) {
assertThat(e.getMessage())
.contains("cannot convert value")
.contains("value type not numeric");
+
+ // assert defaulting
+ assertEquals(99, node.shortValue((short) 99));
}
+
+ // // // Shared helper methods: asShort()
+
+ private void _assertAsShort(short expected, JsonNode node) {
+ assertEquals(expected, node.asShort());
+
+ // and defaulting
+ assertEquals(expected, node.asShort((short) 99));
+ }
+
+ private void _assertAsShortFailForValueRange(JsonNode node) {
+ Exception e = assertThrows(JsonNodeException.class,
+ () -> node.asShort(),
+ "For ("+node.getClass().getSimpleName()+") value: "+node);
+ assertThat(e.getMessage())
+ .contains("asShort()")
+ .contains("cannot convert value")
+ .contains("value not in 16-bit `short` range");
+
+ // assert defaulting
+ assertEquals(99, node.asShort((short) 99));
+ }
+
+ private void _assertAsShortFailForNonNumber(JsonNode node) {
+ _assertAsShortFailForNonNumber(node, "value type not numeric");
+ }
+
+ private void _assertAsShortFailForNonNumber(JsonNode node, String extraFailMsg) {
+ Exception e = assertThrows(JsonNodeException.class,
+ () -> node.asShort(),
+ "For ("+node.getClass().getSimpleName()+") value: "+node);
+ assertThat(e.getMessage())
+ .contains("asShort()")
+ .contains("cannot convert value")
+ .contains(extraFailMsg);
+
+ // assert defaulting
+ assertEquals(99, node.asShort((short) 99));
+ }
+
+ private void _assertAsShortFailForNaN(JsonNode node) {
+ Exception e = assertThrows(JsonNodeException.class,
+ () -> node.asShort(),
+ "For ("+node.getClass().getSimpleName()+") value: "+node);
+ assertThat(e.getMessage())
+ .contains("asShort()")
+ .contains("cannot convert value")
+ .contains("value non-Finite");
+
+ // Verify default value handling
+ assertEquals(99, node.asShort((short) 99));
+ }
+
+
}
diff --git a/src/test/java/tools/jackson/databind/node/POJONodeTest.java b/src/test/java/tools/jackson/databind/node/POJONodeTest.java
index 6fe01159e6..fe0b6784bf 100644
--- a/src/test/java/tools/jackson/databind/node/POJONodeTest.java
+++ b/src/test/java/tools/jackson/databind/node/POJONodeTest.java
@@ -74,4 +74,5 @@ public void testAddJava8DateAsPojo() throws Exception
String msg = result.path("test").asString();
assertEquals(dt, LocalDateTime.parse(msg));
}
+
}