1 package com.fasterxml.jackson.databind.ser.std;
2
3 import java.io.IOException;
4 import java.lang.reflect.Type;
5 import java.math.BigDecimal;
6 import java.util.Map;
7
8 import com.fasterxml.jackson.annotation.JsonFormat;
9
10 import com.fasterxml.jackson.core.*;
11 import com.fasterxml.jackson.core.type.WritableTypeId;
12 import com.fasterxml.jackson.databind.*;
13 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
14 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
15 import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
16 import com.fasterxml.jackson.databind.ser.ContextualSerializer;
17
18 /**
19  * Container class for serializers used for handling standard JDK-provided
20  * primitve number types and their wrapper counterparts (like {@link java.lang.Integer}).
21  */

22 @SuppressWarnings("serial")
23 public class NumberSerializers {
24     protected NumberSerializers() { }
25
26     public static void addAll(Map<String, JsonSerializer<?>> allDeserializers) {
27         allDeserializers.put(Integer.class.getName(), new IntegerSerializer(Integer.class));
28         allDeserializers.put(Integer.TYPE.getName(), new IntegerSerializer(Integer.TYPE));
29         allDeserializers.put(Long.class.getName(), new LongSerializer(Long.class));
30         allDeserializers.put(Long.TYPE.getName(), new LongSerializer(Long.TYPE));
31
32         allDeserializers.put(Byte.class.getName(), IntLikeSerializer.instance);
33         allDeserializers.put(Byte.TYPE.getName(), IntLikeSerializer.instance);
34         allDeserializers.put(Short.class.getName(), ShortSerializer.instance);
35         allDeserializers.put(Short.TYPE.getName(), ShortSerializer.instance);
36
37         // Numbers, limited length floating point
38         allDeserializers.put(Double.class.getName(), new DoubleSerializer(Double.class));
39         allDeserializers.put(Double.TYPE.getName(), new DoubleSerializer(Double.TYPE));
40         allDeserializers.put(Float.class.getName(), FloatSerializer.instance);
41         allDeserializers.put(Float.TYPE.getName(), FloatSerializer.instance);
42     }
43
44     /*
45     /**********************************************************
46     /* Shared base class
47     /**********************************************************
48      */

49
50     /**
51      * Shared base class for actual primitive/wrapper number serializers.
52      * Note that this class is not meant as general-purpose base class nor
53      * is it part of public API: you may extend it with the caveat that not
54      * being part of public API its implementation and interfaces may change
55      * in minor releases; however deprecation markers will be used to allow
56      * code evolution.
57      *<p>
58      * NOTE: {@code public} since 2.10: previously had {@code protected} access.
59      */

60     public abstract static class Base<T> extends StdScalarSerializer<T>
61             implements ContextualSerializer
62     {
63         protected final JsonParser.NumberType _numberType;
64         protected final String _schemaType;
65         protected final boolean _isInt;
66
67         protected Base(Class<?> cls, JsonParser.NumberType numberType,
68                 String schemaType) {
69             super(cls, false);
70             _numberType = numberType;
71             _schemaType = schemaType;
72             _isInt = (numberType == JsonParser.NumberType.INT)
73                     || (numberType == JsonParser.NumberType.LONG)
74                     || (numberType == JsonParser.NumberType.BIG_INTEGER);
75         }
76
77         @Override
78         public JsonNode getSchema(SerializerProvider provider, Type typeHint) {
79             return createSchemaNode(_schemaType, true);
80         }
81
82         @Override
83         public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor,
84                 JavaType typeHint) throws JsonMappingException
85         {
86             if (_isInt) {
87                 visitIntFormat(visitor, typeHint, _numberType);
88             } else {
89                 visitFloatFormat(visitor, typeHint, _numberType);
90             }
91         }
92
93         @Override
94         public JsonSerializer<?> createContextual(SerializerProvider prov,
95                 BeanProperty property) throws JsonMappingException
96         {
97             JsonFormat.Value format = findFormatOverrides(prov, property, handledType());
98             if (format != null) {
99                 switch (format.getShape()) {
100                 case STRING:
101                     if (((Class<?>) handledType()) == BigDecimal.class) {
102                         return NumberSerializer.bigDecimalAsStringSerializer();
103                     }
104                     return ToStringSerializer.instance;
105                 default:
106                 }
107             }
108             return this;
109         }
110     }
111
112     /*
113      *************************************************************
114      * Concrete serializers, numerics
115      *************************************************************
116      */

117
118     @JacksonStdImpl
119     public static class ShortSerializer extends Base<Object> {
120         final static ShortSerializer instance = new ShortSerializer();
121
122         public ShortSerializer() {
123             super(Short.class, JsonParser.NumberType.INT, "number");
124         }
125
126         @Override
127         public void serialize(Object value, JsonGenerator gen,
128                 SerializerProvider provider) throws IOException {
129             gen.writeNumber(((Short) value).shortValue());
130         }
131     }
132
133     /**
134      * This is the special serializer for regular {@link java.lang.Integer}s
135      * (and primitive ints)
136      * <p>
137      * Since this is one of "natural" types, no type information is ever included
138      * on serialization (unlike for most scalar types, except for {@code double}).
139      * <p>
140      * NOTE: as of 2.6, generic signature changed to Object, to avoid generation
141      * of bridge methods.
142      */

143     @JacksonStdImpl
144     public static class IntegerSerializer extends Base<Object> {
145         public IntegerSerializer(Class<?> type) {
146             super(type, JsonParser.NumberType.INT, "integer");
147         }
148
149         @Override
150         public void serialize(Object value, JsonGenerator gen,
151                 SerializerProvider provider) throws IOException {
152             gen.writeNumber(((Integer) value).intValue());
153         }
154
155         // IMPORTANT: copied from `NonTypedScalarSerializerBase`
156         @Override
157         public void serializeWithType(Object value, JsonGenerator gen,
158                 SerializerProvider provider, TypeSerializer typeSer)
159                 throws IOException {
160             // no type info, just regular serialization
161             serialize(value, gen, provider);
162         }
163     }
164
165     /**
166      * Similar to {@link IntegerSerializer}, but will not cast to Integer:
167      * instead, cast is to {@link java.lang.Number}, and conversion is by
168      * calling {@link java.lang.Number#intValue}.
169      */

170     @JacksonStdImpl
171     public static class IntLikeSerializer extends Base<Object> {
172         final static IntLikeSerializer instance = new IntLikeSerializer();
173
174         public IntLikeSerializer() {
175             super(Number.class, JsonParser.NumberType.INT, "integer");
176         }
177
178         @Override
179         public void serialize(Object value, JsonGenerator gen,
180                 SerializerProvider provider) throws IOException {
181             gen.writeNumber(((Number) value).intValue());
182         }
183     }
184
185     @JacksonStdImpl
186     public static class LongSerializer extends Base<Object> {
187         public LongSerializer(Class<?> cls) {
188             super(cls, JsonParser.NumberType.LONG, "number");
189         }
190
191         @Override
192         public void serialize(Object value, JsonGenerator gen,
193                 SerializerProvider provider) throws IOException {
194             gen.writeNumber(((Long) value).longValue());
195         }
196     }
197
198     @JacksonStdImpl
199     public static class FloatSerializer extends Base<Object> {
200         final static FloatSerializer instance = new FloatSerializer();
201
202         public FloatSerializer() {
203             super(Float.class, JsonParser.NumberType.FLOAT, "number");
204         }
205
206         @Override
207         public void serialize(Object value, JsonGenerator gen,
208                 SerializerProvider provider) throws IOException {
209             gen.writeNumber(((Float) value).floatValue());
210         }
211     }
212
213     /**
214      * This is the special serializer for regular {@link java.lang.Double}s (and
215      * primitive doubles)
216      * <p>
217      * Since this is one of "native" types, no type information is ever included
218      * on serialization (unlike for most scalar types other than {@code long}).
219      */

220     @JacksonStdImpl
221     public static class DoubleSerializer extends Base<Object> {
222         public DoubleSerializer(Class<?> cls) {
223             super(cls, JsonParser.NumberType.DOUBLE, "number");
224         }
225
226         @Override
227         public void serialize(Object value, JsonGenerator gen,
228                 SerializerProvider provider) throws IOException {
229             gen.writeNumber(((Double) value).doubleValue());
230         }
231
232         // IMPORTANT: copied from `NonTypedScalarSerializerBase`
233         @Override
234         public void serializeWithType(Object value, JsonGenerator g,
235                 SerializerProvider provider, TypeSerializer typeSer)
236                 throws IOException {
237             // 08-Feb-2018, tatu: Except that as per [databind#2236], NaN values need
238             //    special handling
239             Double d = (Double) value;
240             if (notFinite(d)) {
241                 WritableTypeId typeIdDef = typeSer.writeTypePrefix(g,
242                         // whether to indicate it's number or string is arbitrary; important it is scalar
243                         typeSer.typeId(value, JsonToken.VALUE_NUMBER_FLOAT));
244                 g.writeNumber(d);
245                 typeSer.writeTypeSuffix(g, typeIdDef);
246             } else {
247                 g.writeNumber(d);
248             }
249         }
250
251         public static boolean notFinite(double value) {
252             // `jackson-core` has helper method in 3 but not yet
253             return Double.isNaN(value) || Double.isInfinite(value);
254         }
255     }
256 }
257