Skip to content

DeserializationFeature.ACCEPT_FLOAT_AS_INT not respected for byte/short when deserializing from JsonNode #5340

@Artur-

Description

@Artur-

(continuation of #5319 )

The DeserializationFeature.ACCEPT_FLOAT_AS_INT configuration is not respected when deserializing from a JsonNode to byte or short types using TreeTraversingParser. This issue does NOT affect int or long types, which work correctly. The feature works correctly for all integral types when deserializing directly from a string or stream.

Version Information

3.0.0

Reproduction

import tools.jackson.databind.DeserializationFeature;
import tools.jackson.databind.JsonNode;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.json.JsonMapper;

public class AcceptFloatAsIntBugDemo {
    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = JsonMapper.builder()
            .enable(DeserializationFeature.ACCEPT_FLOAT_AS_INT)
            .build();

        // Test 1: Direct deserialization from string - WORKS
        System.out.println("Test 1: Direct string to byte");
        try {
            byte result = mapper.readValue("1.1", byte.class);
            System.out.println("  ✓ Success: " + result);
        } catch (Exception e) {
            System.out.println("  ✗ Failed: " + e.getMessage());
        }

        // Test 2: Deserialization from JsonNode to byte - FAILS
        System.out.println("\nTest 2: JsonNode to byte");
        try {
            JsonNode node = mapper.readTree("{\"value\": 1.1}");
            JsonNode valueNode = node.get("value");
            byte result = mapper.readerFor(byte.class).readValue(valueNode);
            System.out.println("  ✓ Success: " + result);
        } catch (Exception e) {
            System.out.println("  ✗ Failed: " + e.getMessage());
        }

        // Test 3: JsonNode to short - FAILS
        System.out.println("\nTest 3: JsonNode to short");
        try {
            JsonNode node = mapper.readTree("{\"value\": 1.1}");
            JsonNode valueNode = node.get("value");
            short result = mapper.readerFor(short.class).readValue(valueNode);
            System.out.println("  ✓ Success: " + result);
        } catch (Exception e) {
            System.out.println("  ✗ Failed: " + e.getMessage());
        }

        // Test 4: JsonNode to int - WORKS
        System.out.println("\nTest 4: JsonNode to int");
        try {
            JsonNode node = mapper.readTree("{\"value\": 1.1}");
            JsonNode valueNode = node.get("value");
            int result = mapper.readerFor(int.class).readValue(valueNode);
            System.out.println("  ✓ Success: " + result);
        } catch (Exception e) {
            System.out.println("  ✗ Failed: " + e.getMessage());
        }

        // Test 5: JsonNode to long - WORKS
        System.out.println("\nTest 5: JsonNode to long");
        try {
            JsonNode node = mapper.readTree("{\"value\": 1.1}");
            JsonNode valueNode = node.get("value");
            long result = mapper.readerFor(long.class).readValue(valueNode);
            System.out.println("  ✓ Success: " + result);
        } catch (Exception e) {
            System.out.println("  ✗ Failed: " + e.getMessage());
        }

        // Test 6: Workaround - convert JsonNode to string first - WORKS
        System.out.println("\nTest 6: JsonNode -> String -> byte (workaround)");
        try {
            JsonNode node = mapper.readTree("{\"value\": 1.1}");
            JsonNode valueNode = node.get("value");
            byte result = mapper.readerFor(byte.class).readValue(valueNode.toString());
            System.out.println("  ✓ Success: " + result);
        } catch (Exception e) {
            System.out.println("  ✗ Failed: " + e.getMessage());
        }
    }
}

Expected behavior

All the tests work, but actual output is

Test 1: Direct string to byte
  ✓ Success: 1

Test 2: JsonNode to byte
  ✗ Failed: Numeric value (1.1) of `DoubleNode` has fractional part; cannot convert to `int`

Test 3: JsonNode to short
  ✗ Failed: Numeric value (1.1) of `DoubleNode` has fractional part; cannot convert to `int`

Test 4: JsonNode to int
  ✓ Success: 1

Test 5: JsonNode to long
  ✓ Success: 1

Test 6: JsonNode -> String -> byte (workaround)
  ✓ Success: 1

Additional context

Stack Trace

tools.jackson.core.exc.InputCoercionException: Numeric value (1.1) of `DoubleNode` has fractional part; cannot convert to `int`
 at [No location information]
        at tools.jackson.core.base.ParserMinimalBase._constructInputCoercion(ParserMinimalBase.java:1130)
        at tools.jackson.databind.node.TreeTraversingParser.getIntValue(TreeTraversingParser.java:303)
        at tools.jackson.core.base.ParserMinimalBase.getByteValue(ParserMinimalBase.java:576)
        at tools.jackson.databind.deser.std.StdDeserializer._parseBytePrimitive(StdDeserializer.java:527)
        at tools.jackson.databind.deser.jdk.NumberDeserializers$ByteDeserializer.deserialize(NumberDeserializers.java:259)

Root Cause

The issue appears to be in TreeTraversingParser.getIntValue() which doesn't check or respect the ACCEPT_FLOAT_AS_INT feature when reading from numeric nodes. The error message reveals the problem:

Numeric value (1.1) of `DoubleNode` has fractional part; cannot convert to `int`

Notice it says "cannot convert to int" even when deserializing to byte or short. The stack trace shows:

at tools.jackson.databind.node.TreeTraversingParser.getIntValue(TreeTraversingParser.java:303)
at tools.jackson.core.base.ParserMinimalBase.getByteValue(ParserMinimalBase.java:576)

The methods getByteValue() and getShortValue() internally call getIntValue() first, which validates that the number can be converted to an exact integral value without checking the ACCEPT_FLOAT_AS_INT feature.

By contrast, deserialization to int and long appears to have proper feature checking, which is why those work correctly.

Metadata

Metadata

Assignees

Labels

3.0Issue planned for initial 3.0 release

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions