Skip to content

add asShort(), asFloat() conversion methods with tests #5138

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
May 8, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions src/main/java/tools/jackson/databind/JsonNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,21 @@ 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 it (that is, its value fits
* within Java's 16-bit signed integer type, <code>short</code> and
* if it is a floating-point number, it does not have fractional part).
*<p>
* 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.
*
* @since 2.0
*/
public boolean canConvertToShort() { return false; }

/**
* Method that can be used to check whether this node is a numeric
* node ({@link #isNumber} would return true)
Expand Down Expand Up @@ -740,6 +755,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):
* <ul>
* <li>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).
* </li>
* <li>JSON Strings that represent JSON Numbers ("stringified" numbers)
* </li>
* <li>JSON Null (converted to {@code 0}))
* </li>
* <li>POJO nodes that contain Number values
* </li>
* </ul>
*
* @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

/**
Expand Down Expand Up @@ -1023,6 +1083,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:
* <ul>
* <li>JSON String that represents JSON Numbers ("stringified" numbers)
* </li>
* <li>JSON Null (converted to {@code 0.0f})
* </li>
* <li>POJO nodes that contain Number values
* </li>
* </ul>
*<p>
*
* @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

/**
Expand Down
34 changes: 34 additions & 0 deletions src/main/java/tools/jackson/databind/node/BaseJsonNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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");
Expand Down
64 changes: 55 additions & 9 deletions src/main/java/tools/jackson/databind/node/BigIntegerNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ 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 static BigInteger MIN_SHORT = BigInteger.valueOf(Short.MIN_VALUE);
final static BigInteger MAX_SHORT = BigInteger.valueOf(Short.MAX_VALUE);
final static BigInteger MIN_INTEGER = BigInteger.valueOf(Integer.MIN_VALUE);
final static BigInteger MAX_INTEGER = BigInteger.valueOf(Integer.MAX_VALUE);
final static BigInteger MIN_LONG = BigInteger.valueOf(Long.MIN_VALUE);
final static BigInteger MAX_LONG = BigInteger.valueOf(Long.MAX_VALUE);

final protected BigInteger _value;

Expand Down Expand Up @@ -72,6 +74,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()) {
Expand Down Expand Up @@ -166,6 +194,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();
Expand Down Expand Up @@ -252,11 +301,8 @@ 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(MIN_SHORT) >= 0)
&& (_value.compareTo(MAX_SHORT) <= 0);
}

@Override
Expand Down
5 changes: 0 additions & 5 deletions src/main/java/tools/jackson/databind/node/BooleanNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,6 @@ protected String _asString() {
return _value ? "true" : "false";
}

@Override
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch!

public long asLong(long defaultValue) {
return _value ? 1L : 0L;
}

/*
/**********************************************************************
/* Overridden JsonNode methods, other
Expand Down
39 changes: 33 additions & 6 deletions src/main/java/tools/jackson/databind/node/DecimalNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ 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 static BigDecimal MIN_SHORT = BigDecimal.valueOf(Short.MIN_VALUE);
final static BigDecimal MAX_SHORT = BigDecimal.valueOf(Short.MAX_VALUE);
final static BigDecimal MIN_INTEGER = BigDecimal.valueOf(Integer.MIN_VALUE);
final static BigDecimal MAX_INTEGER = BigDecimal.valueOf(Integer.MAX_VALUE);
final static BigDecimal MIN_LONG = BigDecimal.valueOf(Long.MIN_VALUE);
final static BigDecimal MAX_LONG = BigDecimal.valueOf(Long.MAX_VALUE);

final protected BigDecimal _value;

Expand Down Expand Up @@ -87,6 +87,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();
Expand Down
27 changes: 27 additions & 0 deletions src/main/java/tools/jackson/databind/node/DoubleNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/tools/jackson/databind/node/FloatNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading