Skip to content

Instant precision should be retained in ObjectMapper#readTree (add JsonNodeFeature to force use of BigDecimal for JsonNode reads) #307

Open
@jzheaux

Description

@jzheaux

Describe the bug

ObjectMapper#readValue(String) seems to preserve all nine digits of an Instant's decimal, but ObjectMapper#readTree(JsonParser) does not.

Version information

Jackson 2.12.1

To Reproduce

ObjectMapper mapper = new ObjectMapper();
{
	mapper.registerModule(new JavaTimeModule());
}

Instant issuedAt = Instant.ofEpochSecond(1234567890).plusNanos(123456789);
Map<String, Instant> attributes = new HashMap<>();
{
	attributes.put("iat", issuedAt);
}

@Test
void passes() throws Exception {
	String serialized = mapper.writeValueAsString(attributes);
	Map<String, Instant> deserialized = mapper.readValue(serialized, new TypeReference<>() {});
	assertThat(deserialized.get("iat")).isEqualTo(issuedAt);
}

@Test
void fails() throws Exception {
	String serialized = mapper.writeValueAsString(attributes);
	JsonParser parser = mapper.createParser(serialized);
	JsonNode mapNode = mapper.readTree(parser);
	Map<String, Object> deserialized = new LinkedHashMap<>();
	Iterable<Map.Entry<String, JsonNode>> fields = mapNode::fields;
	for (Map.Entry<String, JsonNode> field : fields) {
		deserialized.put(field.getKey(), mapper.readValue(field.getValue().traverse(mapper), Instant.class));
	}
	assertThat(deserialized.get("iat")).isEqualTo(issuedAt);
}

The second test fails with:

Expecting:
 <2009-02-13T23:31:30.123456700Z>
to be equal to:
 <2009-02-13T23:31:30.123456789Z>
but was not.
Expected :2009-02-13T23:31:30.123456789Z
Actual   :2009-02-13T23:31:30.123456700Z

Additional context

The failure case is derived from a custom deserializer in our project.

Ideally, I'd like readTree to retain the Instant's precision in the same way that ObjectMapper#readValue does when it deserializes a Map that contains Instants, though I'd be happy with advice on how to change my approach in my deserializer instead.

I realize also that USE_BIG_DECIMAL_FOR_FLOATS addresses this issue, but I'd prefer to avoid that, since others import our module.

Metadata

Metadata

Assignees

No one assigned

    Labels

    2.17has-failing-testIndicates that there exists a test case (under `failing/`) to reproduce the issue

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions