Jackson 2.15.0 - Error deserializing an @JsonUnwrapped object with a field of type float
amombourquette opened this issue · 6 comments
This works as of Jackson 2.14.2, and fails with Jackson 2.15.0, both using bson4jackson 2.13.1
Example class being deserialized: (groovy syntax)
static class OuterClass {
@JsonUnwrapped
public InnerClass inner = new InnerClass()
OuterClass() {}
}
static class InnerClass {
public float floatValue = 40.0f
public InnerClass() {}
}
Failing test (but passes with older jackson):
OuterClass outer = new OuterClass()
outer.inner.floatValue = 50.0f
ByteArrayOutputStream baos = new ByteArrayOutputStream()
ObjectMapper BSON_MAPPER = new ObjectMapper(new BsonFactory())
BSON_MAPPER.writeValue(baos, outer)
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray())
OuterClass clone = BSON_MAPPER.readValue(bais, OuterClass.class)
assertThat(clone.inner.floatValue).isEqualTo(outer.inner.floatValue)
I am uncertain if the actual bug exists in jackson or bson4jackson, but the following test using only jackson passes for both old and new versions: (This will use a ReaderBasedJsonParser instead of a BsonParser)
ObjectMapper MAPPER = new ObjectMapper()
OuterClass outer = MAPPER.readValue("{\"floatValue\":50.0}", OuterClass.class)
assertThat(outer.inner.floatValue).isEqualTo(50.0f)
One relevant change in jackson is in TokenBuffer._copyBufferValue(JsonParser p, JsonToken t)
2.14 version vs 2.15 version
2.14 version calls JsonParser.getNumberValueExact() which was overridden in BsonParser, which gets the value out of BsonParser.Context.value
2.15 version calls JsonParser.getNumberValueDeferred() which is not implemented by BsonParser, so it calls ParserBase.
getNumberValueDeferred(), which gets the value from _numberString, _numberFloat, _textBuffer as needed.
Hi @michel-kraemer , I wonder if you have any time to let me know your thoughts on this? Thanks!
com.fasterxml.jackson.databind.JsonMappingException: empty String (through reference chain: com.apptegic.excavate.util.JacksonUnwrappedTest$InnerClass["floatValue"])
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:402)
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:361)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.wrapAndThrow(BeanDeserializerBase.java:1830)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:394)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:185)
at com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:138)
at com.fasterxml.jackson.databind.deser.impl.UnwrappedPropertyHandler.processUnwrapped(UnwrappedPropertyHandler.java:62)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeWithUnwrapped(BeanDeserializer.java:765)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:347)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:185)
at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:323)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4825)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3809)
Caused by: java.lang.NumberFormatException: empty String
at java.base/jdk.internal.math.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1842)
at java.base/jdk.internal.math.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
at java.base/java.lang.Double.parseDouble(Double.java:543)
at com.fasterxml.jackson.core.io.NumberInput.parseDouble(NumberInput.java:388)
at com.fasterxml.jackson.databind.util.TokenBuffer$Parser.getNumberValue(TokenBuffer.java:1936)
at com.fasterxml.jackson.databind.util.TokenBuffer$Parser.getNumberValue(TokenBuffer.java:1891)
at com.fasterxml.jackson.databind.util.TokenBuffer$Parser.getFloatValue(TokenBuffer.java:1847)
at com.fasterxml.jackson.databind.deser.std.NumberDeserializers$FloatDeserializer.deserialize(NumberDeserializers.java:596)
at com.fasterxml.jackson.databind.deser.std.NumberDeserializers$FloatDeserializer.deserialize(NumberDeserializers.java:579)
at com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:138)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:392)
... 35 more
Encountering the same issue with polymorphic deserialization using JsonTypeInfo.As.PROPERTY
, if the first property that is encountered is not the type property. This scenario leads to use of the TokenBuffer
which calls getNumberValueDeferred
on ParserBase
because BsonParser
has no implementation for it yet.
The default implementation in JsonParser
just calls getNumberValue
but it is overridden in ParserBase (from which BsonParser
extends). Is it an idea to override getNumberValueDeferred
in BsonParser
to restore the default getNumberValue
implementation?
getNumberValueDeferred:689, ParserBase (com.fasterxml.jackson.core.base)
_copyBufferValue:1274, TokenBuffer (com.fasterxml.jackson.databind.util)
copyCurrentStructure:1194, TokenBuffer (com.fasterxml.jackson.databind.util)
deserializeTypedFromObject:143, AsPropertyTypeDeserializer (com.fasterxml.jackson.databind.jsontype.impl)
deserializeWithType:1296, BeanDeserializerBase (com.fasterxml.jackson.databind.deser)
deserialize:74, TypeWrappedDeserializer (com.fasterxml.jackson.databind.deser.impl)
readRootValue:323, DefaultDeserializationContext (com.fasterxml.jackson.databind.deser)
_readMapAndClose:4825, ObjectMapper (com.fasterxml.jackson.databind)
readValue:3833, ObjectMapper (com.fasterxml.jackson.databind)
The same issue appears in newer mongojack versions. There is a fix: mongojack/mongojack#241
Nice work! Thanks for the PR, @bernd! I've just published version 2.15.0 to fix this issue.