-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Description
Search before asking
- I searched in the issues and found nothing similar.
Describe the bug
TreeTraversingParser in Jackson 3.0.0-rc9 does not respect the DeserializationFeature.ACCEPT_FLOAT_AS_INT setting when deserializing from JsonNode objects, even though the feature is enabled and works correctly when deserializing from JSON strings.
When DeserializationFeature.ACCEPT_FLOAT_AS_INT is enabled (which is the default in Jackson 3), deserializing a floating-point number to an integer type should truncate the decimal part. This works correctly when deserializing from JSON strings, but fails when deserializing from JsonNode objects.
The issue is in TreeTraversingParser.getIntValue() method which explicitly checks for fractional parts and throws an exception, regardless of the ACCEPT_FLOAT_AS_INT configuration.
Version Information
- Jackson version: 3.0.0-rc9
- Java version: 21
- Operating System: macOS
Reproduction
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.DeserializationFeature;
import tools.jackson.databind.JsonNode;
import tools.jackson.databind.json.JsonMapper;
public class Jackson3FloatToIntBug {
static class TestBean {
public int value;
}
public static void main(String[] args) throws Exception {
ObjectMapper mapper = JsonMapper.builder()
.enable(DeserializationFeature.ACCEPT_FLOAT_AS_INT) // Explicitly enable (though it's default)
.build();
System.out.println("ACCEPT_FLOAT_AS_INT enabled: " +
mapper.isEnabled(DeserializationFeature.ACCEPT_FLOAT_AS_INT));
// Test 1: Direct JSON string parsing - WORKS
try {
TestBean bean1 = mapper.readValue("{\"value\": 1.9}", TestBean.class);
System.out.println("From JSON string: " + bean1.value + " (SUCCESS)");
} catch (Exception e) {
System.out.println("From JSON string: FAILED - " + e.getMessage());
}
// Test 2: Via JsonNode - FAILS
try {
JsonNode node = mapper.readTree("{\"value\": 1.9}");
TestBean bean2 = mapper.treeToValue(node, TestBean.class);
System.out.println("From JsonNode: " + bean2.value + " (SUCCESS)");
} catch (Exception e) {
System.out.println("From JsonNode: FAILED - " + e.getMessage());
}
// Test 3: Via ObjectReader with JsonNode - FAILS
try {
JsonNode node = mapper.readTree("{\"value\": 1.9}");
JsonNode valueNode = node.get("value");
int value = mapper.readerFor(int.class).readValue(valueNode);
System.out.println("From JsonNode field: " + value + " (SUCCESS)");
} catch (Exception e) {
System.out.println("From JsonNode field: FAILED - " + e.getMessage());
}
}
}
Expected behavior
All three tests should succeed and convert 1.9 to 1 (truncating the decimal part) since ACCEPT_FLOAT_AS_INT is enabled.
Actual behavior:
ACCEPT_FLOAT_AS_INT enabled: true
From JSON string: 1 (SUCCESS)
From JsonNode: FAILED - Numeric value (1.9) of `DoubleNode` has fractional part; cannot convert to `int` (through reference chain: TestBean["value"])
From JsonNode field: FAILED - Numeric value (1.9) out of range of `int` (-2147483648 - 2147483647)
The same test with Jackson 2:
ACCEPT_FLOAT_AS_INT enabled: true
From JSON string: 1 (SUCCESS)
From JsonNode: 1 (SUCCESS)
From JsonNode field: 1 (SUCCESS)
Additional context
Root Cause Analysis
The issue is in tools.jackson.databind.node.TreeTraversingParser.getIntValue() (around line 298-309):
@Override
public int getIntValue() throws InputCoercionException {
final NumericNode node = (NumericNode) currentNumericNode(NR_INT);
if (!node.canConvertToInt()) {
// [databind#5309] Misleading exception message for DoubleNode to `int` value conversion
if (!node.canConvertToExactIntegral()) {
throw _constructInputCoercion(String.format(
"Numeric value (%s) of `%s` has fractional part; cannot convert to `int`",
_longIntegerDesc(node.asString()),
node.getClass().getSimpleName()
),
node.asToken(), Integer.TYPE);
}
// ... overflow handling
}
return node.intValue();
}This method checks canConvertToExactIntegral() which returns false for nodes with fractional parts, and immediately throws an exception without consulting the ACCEPT_FLOAT_AS_INT feature flag.