That's great news, thanks Tatu! On Sunday, 10 February 2019 18:34:22 UTC, Tatu Saloranta wrote: > > The issue itself: > > https://github.com/FasterXML/jackson-databind/issues/2236 > > has now been fixed for upcoming 2.10.0 > > -+ Tatu +- > > On Thu, Feb 7, 2019 at 8:10 PM Tatu Saloranta <[email protected] > <javascript:>> wrote: > >> Ok. As to the original question: reading and writing floating-point >> values is pretty slow, in general, so I suspect your approach is not >> overly inefficient. >> (there was a recent post by someone pointing to some >> higher-performance new implementation, which might be interesting -- >> but still, binary floating point to/from 10-based textual >> representation is one of areas where textual formats are at >> disadvantage). >> >> -+ Tatu +- >> >> On Sun, Jan 27, 2019 at 12:29 PM C-B-B <[email protected] <javascript:>> >> wrote: >> > >> > After much trial and error I've been able to implement a workaround for >> including the type on NaN and infinity Doubles during the serialization of >> the desired Map objects, without getting in the general code path for >> Double serialization. >> > It's a little hacky (in particular I'm not retrieving the TypeResolver >> completely dynamically - advice welcome there) but it might keep me going >> until the Jackson lib supports this, and it should help in case anyone has >> the same use case. >> > Please let me know if you have better ideas! >> > >> > >> > import com.fasterxml.jackson.annotation.JsonTypeInfo; >> > import com.fasterxml.jackson.core.JsonGenerator; >> > import com.fasterxml.jackson.databind.*; >> > import com.fasterxml.jackson.databind.annotation.JsonSerialize; >> > import com.fasterxml.jackson.databind.jsontype.TypeSerializer; >> > import com.fasterxml.jackson.databind.jsontype.impl.ClassNameIdResolver; >> > import >> com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder; >> > import com.fasterxml.jackson.databind.ser.std.MapSerializer; >> > import com.fasterxml.jackson.databind.ser.std.NumberSerializers; >> > import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer; >> > import com.fasterxml.jackson.databind.type.SimpleType; >> > >> > import java.io.IOException; >> > import java.util.Collections; >> > import java.util.HashMap; >> > import java.util.Map; >> > import java.util.Set; >> > >> > import static >> com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap.emptyForProperties; >> > >> > public class Repro { >> > private static ObjectMapper objectMapper = new ObjectMapper(); >> > >> > public static void main(String[] args) { >> > try { >> > String beanString = objectMapper.writeValueAsString(new >> Bean(1L, Double.NaN)); >> > System.out.println(beanString); >> > MapHolder beanOut = objectMapper.readValue(beanString, >> MapHolder.class); >> > System.out.println(beanOut.data.get("double").getClass()); >> > beanString = objectMapper.writeValueAsString(new Bean(1L, >> 1D)); >> > System.out.println(beanString); >> > beanOut = objectMapper.readValue(beanString, >> MapHolder.class); >> > System.out.println(beanOut.data.get("double").getClass()); >> > } catch (IOException e) { >> > e.printStackTrace(); >> > } >> > } >> > >> > public static class Bean { >> > private Long longValue; >> > private Double doubleValue; >> > >> > public Bean(Long longValue, Double doubleValue) { >> > this.longValue = longValue; >> > this.doubleValue = doubleValue; >> > } >> > >> > @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) >> > @JsonSerialize(using = CustomMapSerializer.class) >> > public Map<String, Object> getData() { >> > Map<String, Object> map = new HashMap<>(); >> > map.put("long", longValue); >> > map.put("double", doubleValue); >> > return map; >> > } >> > } >> > >> > public static class MapHolder { >> > @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) >> > public Map<String, Object> data; >> > >> > MapHolder() { >> > } >> > } >> > >> > public static class CustomDoubleSerializer extends >> JsonSerializer<Object> { >> > private NumberSerializers.DoubleSerializer doubleSerializer; >> > private StdScalarSerializer<Object> scalarSerializer; >> > >> > public CustomDoubleSerializer() { >> > this.doubleSerializer = new >> NumberSerializers.DoubleSerializer(Double.class); >> > this.scalarSerializer = new >> StdScalarSerializer<Object>(Object.class) { >> > @Override >> > public void serialize(Object aDouble, JsonGenerator >> jsonGenerator, SerializerProvider serializerProvider) throws IOException { >> > doubleSerializer.serialize(aDouble, jsonGenerator, >> serializerProvider); >> > } >> > }; >> > } >> > >> > @Override >> > public void serialize(Object aDouble, JsonGenerator >> jsonGenerator, SerializerProvider serializerProvider) throws IOException { >> > doubleSerializer.serialize(aDouble, jsonGenerator, >> serializerProvider); >> > } >> > >> > @Override >> > public void serializeWithType(Object aDouble, JsonGenerator >> jsonGenerator, SerializerProvider serializerProvider, TypeSerializer >> typeSerializer) throws IOException { >> > if (aDouble instanceof Double && (((Double) >> aDouble).isInfinite() || ((Double) aDouble).isNaN())) { >> > scalarSerializer.serializeWithType(aDouble, >> jsonGenerator, serializerProvider, typeSerializer); >> > } else { >> > doubleSerializer.serialize(aDouble, jsonGenerator, >> serializerProvider); >> > } >> > } >> > } >> > >> > >> > public static class CustomMapSerializer extends MapSerializer { >> > /* Should be used for the initial instantiation, but >> overwritten by createContextual */ >> > CustomMapSerializer() { >> > super(Collections.emptySet(), >> SimpleType.constructUnsafe(String.class), >> SimpleType.constructUnsafe(Object.class), >> > false, null, null, null); >> > } >> > >> > CustomMapSerializer(MapSerializer src) { >> > this(src, null, false); >> > this._dynamicValueSerializers = >> emptyForProperties().addSerializer(Double.class, new >> CustomDoubleSerializer()).map; >> > } >> > >> > CustomMapSerializer(MapSerializer src, Object filterId, boolean >> sortKeys) { >> > super(src, filterId, sortKeys); >> > this._dynamicValueSerializers = >> emptyForProperties().addSerializer(Double.class, new >> CustomDoubleSerializer()).map; >> > } >> > >> > CustomMapSerializer(MapSerializer src, TypeSerializer vts, >> Object suppressableValue, boolean suppressNulls) { >> > super(src, vts, suppressableValue, suppressNulls); >> > this._dynamicValueSerializers = >> emptyForProperties().addSerializer(Double.class, new >> CustomDoubleSerializer()).map; >> > } >> > >> > CustomMapSerializer(MapSerializer src, BeanProperty property, >> JsonSerializer<?> keySerializer, JsonSerializer<?> valueSerializer, >> Set<String> ignoredEntries) { >> > super(src, property, keySerializer, valueSerializer, >> ignoredEntries); >> > this._dynamicValueSerializers = >> emptyForProperties().addSerializer(Double.class, new >> CustomDoubleSerializer()).map; >> > } >> > >> > @Override >> > public CustomMapSerializer withResolved(BeanProperty property, >> JsonSerializer<?> keySerializer, JsonSerializer<?> valueSerializer, >> Set<String> ignored, boolean sortKeys) { >> > CustomMapSerializer ser = new CustomMapSerializer(this, >> property, keySerializer, valueSerializer, ignored); >> > if (sortKeys != ser._sortKeys) { >> > ser = new CustomMapSerializer(ser, this._filterId, >> sortKeys); >> > } >> > return ser; >> > } >> > >> > @Override >> > public CustomMapSerializer >> _withValueTypeSerializer(TypeSerializer vts) { >> > if (this._valueTypeSerializer == vts) { >> > return this; >> > } else { >> > return new CustomMapSerializer(this, vts, >> this._suppressableValue, this._suppressNulls); >> > } >> > } >> > >> > @Override >> > public JsonSerializer<?> createContextual(SerializerProvider >> provider, BeanProperty property) throws JsonMappingException { >> > JsonSerializer ser = >> provider.findValueSerializer(Map.class); >> > if (ser instanceof MapSerializer) { >> > MapSerializer mapSer = (MapSerializer) ((MapSerializer) >> ser).createContextual(provider, property); >> > JsonTypeInfo typeInfo = >> property.getAnnotation(JsonTypeInfo.class); >> > // Would be good to find a way to retrieve the >> TypeSerializer dynamically... >> > TypeSerializer typeSer = new >> StdTypeResolverBuilder().init(typeInfo.use(), new >> ClassNameIdResolver(SimpleType.constructUnsafe(Object.class), >> provider.getTypeFactory())) >> > >> .inclusion(typeInfo.include()).buildTypeSerializer(provider.getConfig(), >> SimpleType.constructUnsafe(Object.class), Collections.emptyList()); >> > CustomMapSerializer customMapSerializer = new >> CustomMapSerializer(mapSer); >> > return >> customMapSerializer._withValueTypeSerializer(typeSer); >> > } >> > return this; >> > } >> > } >> > } >> > >> > >> > On Sunday, 27 January 2019 10:27:16 UTC, C-B-B wrote: >> >> >> >> Many thanks for your response Tatu - looking forward to a fix. >> >> >> >> A couple more questions on the approach for the workaround: >> >> >> >> I've implemented the below CustomDoubleSerializer - how bad do you >> think the performance impact would be for our Double serialisations, at a >> high level, between "barely noticeable" and "very bad"? >> >> Isn't there a way I can get this to only apply for Map element >> serialisations? >> >> Or even better, create a custom MapSerializer that would behave just >> like the defaut MapSerializer, except when it comes to serializing Doubles? >> If so what's the easiest way to do so? I'm getting a little lost when >> trying to do that! >> >> >> >> Many thanks, >> >> CBB >> >> >> >> PS : apologies for the dark code background, not sure how best to >> include code snippets >> >> >> >> import com.fasterxml.jackson.core.JsonGenerator; >> >> import com.fasterxml.jackson.databind.JsonSerializer; >> >> import com.fasterxml.jackson.databind.SerializerProvider; >> >> import com.fasterxml.jackson.databind.jsontype.TypeSerializer; >> >> import com.fasterxml.jackson.databind.ser.std.NumberSerializers; >> >> import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer; >> >> >> >> import java.io.IOException; >> >> >> >> public class CustomDoubleSerializer extends JsonSerializer<Double> { >> >> private NumberSerializers.DoubleSerializer doubleSerializer; >> >> private StdScalarSerializer<Double> scalarSerializer; >> >> >> >> public CustomDoubleSerializer() { >> >> this.doubleSerializer = new >> NumberSerializers.DoubleSerializer(Double.class); >> >> this.scalarSerializer = new >> StdScalarSerializer<Double>(Double.class) { >> >> @Override >> >> public void serialize(Double aDouble, JsonGenerator >> jsonGenerator, SerializerProvider serializerProvider) throws IOException { >> >> doubleSerializer.serialize(aDouble, jsonGenerator, >> serializerProvider); >> >> } >> >> }; >> >> } >> >> >> >> @Override >> >> public void serialize(Double aDouble, JsonGenerator jsonGenerator, >> SerializerProvider serializerProvider) throws IOException { >> >> doubleSerializer.serialize(aDouble, jsonGenerator, >> serializerProvider); >> >> } >> >> >> >> @Override >> >> public void serializeWithType(Double aDouble, JsonGenerator >> jsonGenerator, SerializerProvider serializerProvider, TypeSerializer >> typeSerializer) throws IOException { >> >> if (aDouble != null && (aDouble.isInfinite() || >> aDouble.isNaN())) { >> >> scalarSerializer.serializeWithType(aDouble, jsonGenerator, >> serializerProvider, typeSerializer); >> >> } else { >> >> doubleSerializer.serialize(aDouble, jsonGenerator, >> serializerProvider); >> >> } >> >> } >> >> } >> >> >> >> >> >> >> >> >> >> >> >> >> >> On Sunday, 27 January 2019 07:22:07 UTC, Tatu Saloranta wrote: >> >>> >> >>> On Sat, Jan 26, 2019 at 7:08 PM C-B-B <[email protected]> wrote: >> >>> > >> >>> > Hello folks, >> >>> > >> >>> > I've been trying to serialise and deserialise a Map<String, Object> >> whilst preserving the type of the elements, for example a Long should >> remain a Long and not become an Integer. >> >>> > This works pretty well using the @JsonTypeInfo annotation on the >> Map. Jackson is being clever, and only including the type info when it is >> needed (e.g. it won't include it on an Integer, String, Double etc as they >> are default deserialisation types anyway), which is nice. >> >>> > There's however a nasty edge case with Double.NaN, >> Double.POSITIVE_INFINITY and Double.NEGATIVE_INFINITY: they get serialised >> without type info, and get deserialised as Strings... >> >>> > >> >>> > I've raised this as an issue, but in the meantime I'm looking for a >> workaround to include the type info on these Doubles (I've checked, and the >> deserialisation of NaN and infinity values works fine if the type is >> included). >> >>> > Ideally, the customised code would only be invoked for that Map >> object, rather that for all serialisations (I've been able to override the >> DoubleSerializer altogether on the ObjectMapper, but would rather not make >> such an invasive change). >> >>> >> >>> I agree that this is a bug, and hope to eventually resolve it as per >> >>> issue filed. >> >>> >> >>> But in the meantime I do think that custom deserializer is the way to >> >>> go; and there's no easy way to change to only occur for Map values >> >>> (delegation model means that same DoubleDeserializer is used for >> >>> properties and map values). >> >>> >> >>> -+ Tatu +- >> >>> >> >>> > >> >>> > I've tried a few things, including with TypeIdResolver, but no luck >> so far... Any help would be much appreciated! >> >>> > >> >>> > Here's a little repro >> >>> > >> >>> > import com.fasterxml.jackson.annotation.JsonTypeInfo; >> >>> > import com.fasterxml.jackson.databind.ObjectMapper; >> >>> > import java.io.IOException; >> >>> > import java.util.HashMap; >> >>> > import java.util.Map; >> >>> > >> >>> > public class Repro { >> >>> > private static ObjectMapper objectMapper = new ObjectMapper(); >> >>> > >> >>> > public static void main(String[] args) { >> >>> > try { >> >>> > String beanString = objectMapper.writeValueAsString(new >> Bean(1L, Double.NaN)); >> >>> > MapHolder beanOut = objectMapper.readValue(beanString, >> MapHolder.class); >> >>> > >> System.out.println(beanOut.data.get("double").getClass()); >> >>> > beanString = objectMapper.writeValueAsString(new >> Bean(1L, 1D)); >> >>> > beanOut = objectMapper.readValue(beanString, >> MapHolder.class); >> >>> > >> System.out.println(beanOut.data.get("double").getClass()); >> >>> > } catch (IOException e) { >> >>> > e.printStackTrace(); >> >>> > } >> >>> > } >> >>> > >> >>> > public static class Bean { >> >>> > private Long longValue; >> >>> > private Double doubleValue; >> >>> > >> >>> > public Bean(Long longValue, Double doubleValue) { >> >>> > this.longValue = longValue; >> >>> > this.doubleValue = doubleValue; >> >>> > } >> >>> > >> >>> > @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = >> JsonTypeInfo.As.PROPERTY, property = "@class") >> >>> > public Map<String, Object> getData() { >> >>> > Map<String, Object> map = new HashMap<>(); >> >>> > map.put("long", longValue); >> >>> > map.put("double", doubleValue); >> >>> > return map; >> >>> > } >> >>> > } >> >>> > >> >>> > public static class MapHolder { >> >>> > public Map<String, Object> data; >> >>> > MapHolder() {} >> >>> > } >> >>> > } >> >>> > >> >>> > >> >>> > >> >>> > -- >> >>> > You received this message because you are subscribed to the Google >> Groups "jackson-user" group. >> >>> > To unsubscribe from this group and stop receiving emails from it, >> send an email to [email protected]. >> >>> > To post to this group, send email to [email protected]. >> >>> > For more options, visit https://groups.google.com/d/optout. >> > >> > -- >> > You received this message because you are subscribed to the Google >> Groups "jackson-user" group. >> > To unsubscribe from this group and stop receiving emails from it, send >> an email to [email protected] <javascript:>. >> > To post to this group, send email to [email protected] >> <javascript:>. >> > For more options, visit https://groups.google.com/d/optout. >> >> -- >> You received this message because you are subscribed to the Google Groups >> "jackson-user" group. >> To unsubscribe from this group and stop receiving emails from it, send an >> email to [email protected] <javascript:>. >> To post to this group, send email to [email protected] >> <javascript:>. >> For more options, visit https://groups.google.com/d/optout. >> >
-- You received this message because you are subscribed to the Google Groups "jackson-user" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To post to this group, send email to [email protected]. For more options, visit https://groups.google.com/d/optout.
