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 <t...@fasterxml.com> 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 <c.bea...@gmail.com> 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 <c.be...@gmail.com> 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 jackson-user...@googlegroups.com.
> >>> > To post to this group, send email to jackso...@googlegroups.com.
> >>> > 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 jackson-user+unsubscr...@googlegroups.com.
> > To post to this group, send email to jackson-user@googlegroups.com.
> > 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 jackson-user+unsubscr...@googlegroups.com.
> To post to this group, send email to jackson-user@googlegroups.com.
> 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 jackson-user+unsubscr...@googlegroups.com.
To post to this group, send email to jackson-user@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to