Map deserialization results in different numeric classes based on json ordering (BigDecimal / Double) when used in combination with @JsonSubTypes
See original GitHub issueDescribe the bug When deserializing a jsonSubTye which contains a map with numeric values (floats) the order of the json string results in different classes constructed.
I have pasted a complete Junit5 Test which can easily be used to reproduce the issue.
Version information Happens from 2.12.0 onwards, in 2.11.x its ok.
To Reproduce If you have a way to reproduce this with:
package test;
import java.util.HashMap;
import java.util.Map;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
@TestInstance(Lifecycle.PER_CLASS)
public class JacksonSubTypeBehaviourTest {
private static final Logger log = LoggerFactory.getLogger(JacksonSubTypeBehaviourTest.class);
private ObjectMapper mapper;
@BeforeAll
public void before() {
mapper = new ObjectMapper();
mapper.disable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
}
@Test
public void testDeserializeWithDifferentOrdering() throws Exception {
String ordering1 = "{\n"
+ " \"type\": \"MAP\",\n"
+ " \"map\": {\n"
+ " \"doubleValue\": 0.1\n"
+ " }\n"
+ "}";
TestMapContainer model1 = mapper.readValue(ordering1, TestMapContainer.class);
log.info("clazz: {}", model1.getMap().get("doubleValue").getClass());
Assertions.assertTrue(model1.getMap().get("doubleValue") instanceof Double);
String ordering2 = "{\n"
+ " \"map\": {\n"
+ " \"doubleValue\": 0.1\n"
+ " },\n"
+ " \"type\": \"MAP\"\n"
+ " \n"
+ "}";
TestMapContainer model2 = mapper.readValue(ordering2, TestMapContainer.class);
log.info("clazz: {}", model2.getMap().get("doubleValue").getClass());
Assertions.assertTrue(model2.getMap().get("doubleValue") instanceof Double);
}
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
@JsonSubTypes({
@Type(value = TestMapContainer.class, name = "MAP"),
})
private static interface TestJsonTypeInfoInterface {
}
private static class TestMapContainer implements TestJsonTypeInfoInterface {
private Map<String, ? extends Object> map = new HashMap<>();
public Map<String, ? extends Object> getMap() {
return map;
}
public void setMap(Map<String, ? extends Object> map) {
this.map = map;
}
}
}
Expected behavior Numeric values (point numbers) should always be created as Double unless stated otherwise with DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS
Issue Analytics
- State:
- Created 2 years ago
- Comments:9 (5 by maintainers)
Top Results From Across the Web
Jackson JSON - Deserializing as a subtype using 'as' attribute ...
The attribute @JsonDeserialize#as can be used to to deserialize to a specific subtype of declared type of a Java object.
Read more >Using unsafe Jackson deserialization configuration is security ...
MINIMAL_CLASS) and so rely on @JsonTypeName and @JsonSubTypes . Sensitive Code Example. ObjectMapper mapper = new ObjectMapper(); mapper.enableDefaultTyping(); ...
Read more >Java to Jackson JSON serialization: Money fields
I was thinking about making a Money class with a custom Jackson serializer... can you make a custom serializer for a field variable?...
Read more >Index (Jackson JSON Processor) - Javadoc.io
Shared base class used for anything on which annotations (included within a ... if possible (when resulting serialization would use JSON Object).
Read more >The Behavioral Diversity of Java JSON Libraries - DiVA Portal
Strings, Numbers, Booleans and null. The composite types are Object types that map String keys to values of any type, and an Array...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
The difference is meant to defer possible truncation/coercion in case of buffering (since we have no idea what the target type is); whereas when using
UntypedObjectDeserializer
– which has target type ofjava.lang.Object
– we know what target type we should be using, based on configuration.This matter more for binary formats where there is specific accurate representation, but is more problematic for textual formats that do not have distinction between various FP representations. And then there is the difference between streaming-level and databind-level configuration to consider too.
Ideally, I think, the whole decoding should be deferred in buffering cases: parser would simply validate that the textual representation is valid JSON (or xml, yaml, csv etc) Number, but would not try to get Java
Number
out of it until explicitly requested.@mreiterer I don’t think so due to timing constraints.