javajacksonjackson-databind

DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS doesn't preserve scale


When I deserialize a JSON number to a BigDecimal using readTree, the results don't preserve the scale, i.e. it treats 0.10 as 0.1. On the other hand, if I deserialize using readValue, it does preserve scale, returning a BigDecimal with the correct scale of 2:

    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
    JsonNode jsonNode = objectMapper.readTree("0.10");
    BigDecimal numberFromReadTree = ((DecimalNode)jsonNode).decimalValue();
    BigDecimal numberFromReadValue = objectMapper.readValue("0.10", BigDecimal.class);

    System.out.println(numberFromReadTree); // Prints 0.1, i.e. scale = 1
    System.out.println(numberFromReadValue); // Prints 0.10, i.e. scale = 2

Is there a reason for this apparent inconsistency, and is there an option I can set to keep the scale the same as the input (i.e. to be consistent with readValue)?


Solution

  • Is there a reason for this apparent inconsistency, and is there an option I can set to keep the scale the same as the input (i.e. to be consistent with readValue)?

    As you pointed the reason for this inconsistency stands in an option that can be set for JSonNode nodes that will be created:

    JsonNodeFactory factory = new JsonNodeFactory(true);
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.setNodeFactory(factory);
    objectMapper.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
    JsonNode jsonNode = objectMapper.readTree("0.10");
    System.out.println(jsonNode); // Prints 0.10, i.e. scale = 2
    

    Basically the ObjectMapper mapper has to set its JSonNodeFactory attribute passing a new JsonNodeFactory object created by the JsonNodeFactory#JsonNodeFactory-boolean constructor using the true value indicating that DecimalNode instances must be built with exact representations of BigDecimal instances, while the std ObjectMapper constructor owns a JsonNodeFactory object created by the no-arg constructor (and the default instance) with a default false value as an argument.

    In the second case that uses a ObjectMapper mapper without touching the JSonNodeFactory attribute:

    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
    BigDecimal numberFromReadValue = objectMapper.readValue("0.10", BigDecimal.class);
    System.out.println(numberFromReadValue); // Prints 0.10, i.e. scale = 2
    

    It works because in the BigDecimal class the BigDecimal.html#BigDecimal-java.lang.String constructor is present and directly used by the ObjectMapper mapper giving the expected result.