1 package com.fasterxml.jackson.databind.ser.std;
2 
3 import java.io.IOException;
4 import java.lang.reflect.InvocationTargetException;
5 import java.lang.reflect.Type;
6 import java.util.Collection;
7 import java.util.IdentityHashMap;
8 import java.util.Map;
9 
10 import com.fasterxml.jackson.annotation.JsonFormat;
11 import com.fasterxml.jackson.annotation.JsonInclude;
12 import com.fasterxml.jackson.core.*;
13 import com.fasterxml.jackson.core.JsonParser.NumberType;
14 import com.fasterxml.jackson.databind.*;
15 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
16 import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
17 import com.fasterxml.jackson.databind.jsonFormatVisitors.*;
18 import com.fasterxml.jackson.databind.jsonschema.SchemaAware;
19 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
20 import com.fasterxml.jackson.databind.node.ObjectNode;
21 import com.fasterxml.jackson.databind.ser.FilterProvider;
22 import com.fasterxml.jackson.databind.ser.PropertyFilter;
23 import com.fasterxml.jackson.databind.util.ClassUtil;
24 import com.fasterxml.jackson.databind.util.Converter;
25 
26 /**
27  * Base class used by all standard serializers, and can also
28  * be used for custom serializers (in fact, this is the recommended
29  * base class to use).
30  * Provides convenience methods for implementing {@link SchemaAware}
31  */
32 public abstract class StdSerializer<T>
33     extends JsonSerializer<T>
34     implements JsonFormatVisitable, SchemaAware, java.io.Serializable
35 {
36     private static final long serialVersionUID = 1L;
37 
38     /**
39      * Key used for storing a lock object to prevent infinite recursion when
40      * constructing converting serializers.
41      *
42      * @since 2.9
43      */
44     private final static Object KEY_CONTENT_CONVERTER_LOCK = new Object();
45 
46     /**
47      * Nominal type supported, usually declared type of
48      * property for which serializer is used.
49      */
50     protected final Class<T> _handledType;
51 
52     /*
53     /**********************************************************
54     /* Life-cycle
55     /**********************************************************
56      */
57 
StdSerializer(Class<T> t)58     protected StdSerializer(Class<T> t) {
59         _handledType = t;
60     }
61 
62     @SuppressWarnings("unchecked")
StdSerializer(JavaType type)63     protected StdSerializer(JavaType type) {
64         _handledType = (Class<T>) type.getRawClass();
65     }
66 
67     /**
68      * Alternate constructor that is (alas!) needed to work
69      * around kinks of generic type handling
70      */
71     @SuppressWarnings("unchecked")
StdSerializer(Class<?> t, boolean dummy)72     protected StdSerializer(Class<?> t, boolean dummy) {
73         _handledType = (Class<T>) t;
74     }
75 
76     /**
77      * @since 2.6
78      */
79     @SuppressWarnings("unchecked")
StdSerializer(StdSerializer<?> src)80     protected StdSerializer(StdSerializer<?> src) {
81         _handledType = (Class<T>) src._handledType;
82     }
83 
84     /*
85     /**********************************************************
86     /* Accessors
87     /**********************************************************
88      */
89 
90     @Override
handledType()91     public Class<T> handledType() { return _handledType; }
92 
93     /*
94     /**********************************************************
95     /* Serialization
96     /**********************************************************
97      */
98 
99     @Override
serialize(T value, JsonGenerator gen, SerializerProvider provider)100     public abstract void serialize(T value, JsonGenerator gen, SerializerProvider provider)
101         throws IOException;
102 
103     /*
104     /**********************************************************
105     /* Type introspection API, partial/default implementation
106     /**********************************************************
107      */
108 
109     /**
110      * Default implementation specifies no format. This behavior is usually
111      * overriden by custom serializers.
112      */
113     @Override
acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)114     public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
115         throws JsonMappingException
116     {
117         visitor.expectAnyFormat(typeHint);
118     }
119 
120     /**
121      * Default implementation simply claims type is "string"; usually
122      * overriden by custom serializers.
123      */
124     @Override
getSchema(SerializerProvider provider, Type typeHint)125     public JsonNode getSchema(SerializerProvider provider, Type typeHint) throws JsonMappingException
126     {
127         return createSchemaNode("string");
128     }
129 
130     /**
131      * Default implementation simply claims type is "string"; usually
132      * overriden by custom serializers.
133      */
134     @Override
getSchema(SerializerProvider provider, Type typeHint, boolean isOptional)135     public JsonNode getSchema(SerializerProvider provider, Type typeHint, boolean isOptional)
136         throws JsonMappingException
137     {
138         ObjectNode schema = (ObjectNode) getSchema(provider, typeHint);
139         if (!isOptional) {
140             schema.put("required", !isOptional);
141         }
142         return schema;
143     }
144 
145     /*
146     /**********************************************************
147     /* Helper methods for JSON Schema generation
148     /**********************************************************
149      */
150 
createSchemaNode(String type)151     protected ObjectNode createSchemaNode(String type)
152     {
153         ObjectNode schema = JsonNodeFactory.instance.objectNode();
154         schema.put("type", type);
155         return schema;
156     }
157 
createSchemaNode(String type, boolean isOptional)158     protected ObjectNode createSchemaNode(String type, boolean isOptional)
159     {
160         ObjectNode schema = createSchemaNode(type);
161         if (!isOptional) {
162             schema.put("required", !isOptional);
163         }
164         return schema;
165     }
166 
167     /**
168      * Helper method that calls necessary visit method(s) to indicate that the
169      * underlying JSON type is JSON String.
170      *
171      * @since 2.7
172      */
visitStringFormat(JsonFormatVisitorWrapper visitor, JavaType typeHint)173     protected void visitStringFormat(JsonFormatVisitorWrapper visitor, JavaType typeHint)
174             throws JsonMappingException {
175         /*JsonStringFormatVisitor v2 =*/ visitor.expectStringFormat(typeHint);
176     }
177 
178     /**
179      * Helper method that calls necessary visit method(s) to indicate that the
180      * underlying JSON type is JSON String, but that there is a more refined
181      * logical type
182      *
183      * @since 2.7
184      */
visitStringFormat(JsonFormatVisitorWrapper visitor, JavaType typeHint, JsonValueFormat format)185     protected void visitStringFormat(JsonFormatVisitorWrapper visitor, JavaType typeHint,
186             JsonValueFormat format)
187         throws JsonMappingException
188     {
189         JsonStringFormatVisitor v2 = visitor.expectStringFormat(typeHint);
190         if (v2 != null) {
191             v2.format(format);
192         }
193     }
194 
195     /**
196      * Helper method that calls necessary visit method(s) to indicate that the
197      * underlying JSON type is JSON Integer number.
198      *
199      * @since 2.7
200      */
visitIntFormat(JsonFormatVisitorWrapper visitor, JavaType typeHint, NumberType numberType)201     protected void visitIntFormat(JsonFormatVisitorWrapper visitor, JavaType typeHint,
202             NumberType numberType)
203         throws JsonMappingException
204     {
205         JsonIntegerFormatVisitor v2 = visitor.expectIntegerFormat(typeHint);
206         if (_neitherNull(v2, numberType)) {
207             v2.numberType(numberType);
208         }
209     }
210 
211     /**
212      * Helper method that calls necessary visit method(s) to indicate that the
213      * underlying JSON type is JSON Integer number, but that there is also a further
214      * format restriction involved.
215      *
216      * @since 2.7
217      */
visitIntFormat(JsonFormatVisitorWrapper visitor, JavaType typeHint, NumberType numberType, JsonValueFormat format)218     protected void visitIntFormat(JsonFormatVisitorWrapper visitor, JavaType typeHint,
219             NumberType numberType, JsonValueFormat format)
220         throws JsonMappingException
221     {
222         JsonIntegerFormatVisitor v2 = visitor.expectIntegerFormat(typeHint);
223         if (v2 != null) {
224             if (numberType != null) {
225                 v2.numberType(numberType);
226             }
227             if (format != null) {
228                 v2.format(format);
229             }
230         }
231     }
232 
233     /**
234      * Helper method that calls necessary visit method(s) to indicate that the
235      * underlying JSON type is a floating-point JSON number.
236      *
237      * @since 2.7
238      */
visitFloatFormat(JsonFormatVisitorWrapper visitor, JavaType typeHint, NumberType numberType)239     protected void visitFloatFormat(JsonFormatVisitorWrapper visitor, JavaType typeHint,
240             NumberType numberType)
241         throws JsonMappingException
242     {
243         JsonNumberFormatVisitor v2 = visitor.expectNumberFormat(typeHint);
244         if (v2 != null) {
245             v2.numberType(numberType);
246         }
247     }
248 
249     /**
250      * @since 2.7
251      */
visitArrayFormat(JsonFormatVisitorWrapper visitor, JavaType typeHint, JsonSerializer<?> itemSerializer, JavaType itemType)252     protected void visitArrayFormat(JsonFormatVisitorWrapper visitor, JavaType typeHint,
253             JsonSerializer<?> itemSerializer, JavaType itemType)
254         throws JsonMappingException
255     {
256         JsonArrayFormatVisitor v2 = visitor.expectArrayFormat(typeHint);
257         if (_neitherNull(v2, itemSerializer)) {
258             v2.itemsFormat(itemSerializer, itemType);
259         }
260     }
261 
262     /**
263      * @since 2.7
264      */
visitArrayFormat(JsonFormatVisitorWrapper visitor, JavaType typeHint, JsonFormatTypes itemType)265     protected void visitArrayFormat(JsonFormatVisitorWrapper visitor, JavaType typeHint,
266             JsonFormatTypes itemType)
267         throws JsonMappingException
268     {
269         JsonArrayFormatVisitor v2 = visitor.expectArrayFormat(typeHint);
270         if (v2 != null) {
271             v2.itemsFormat(itemType);
272         }
273     }
274 
275     /*
276     /**********************************************************
277     /* Helper methods for exception handling
278     /**********************************************************
279      */
280 
281     /**
282      * Method that will modify caught exception (passed in as argument)
283      * as necessary to include reference information, and to ensure it
284      * is a subtype of {@link IOException}, or an unchecked exception.
285      *<p>
286      * Rules for wrapping and unwrapping are bit complicated; essentially:
287      *<ul>
288      * <li>Errors are to be passed as is (if uncovered via unwrapping)
289      * <li>"Plain" IOExceptions (ones that are not of type
290      *   {@link JsonMappingException} are to be passed as is
291      *</ul>
292      */
wrapAndThrow(SerializerProvider provider, Throwable t, Object bean, String fieldName)293     public void wrapAndThrow(SerializerProvider provider,
294             Throwable t, Object bean, String fieldName)
295         throws IOException
296     {
297         /* 05-Mar-2009, tatu: But one nasty edge is when we get
298          *   StackOverflow: usually due to infinite loop. But that
299          *   usually gets hidden within an InvocationTargetException...
300          */
301         while (t instanceof InvocationTargetException && t.getCause() != null) {
302             t = t.getCause();
303         }
304         // Errors and "plain" to be passed as is
305         ClassUtil.throwIfError(t);
306         // Ditto for IOExceptions... except for mapping exceptions!
307         boolean wrap = (provider == null) || provider.isEnabled(SerializationFeature.WRAP_EXCEPTIONS);
308         if (t instanceof IOException) {
309             if (!wrap || !(t instanceof JsonMappingException)) {
310                 throw (IOException) t;
311             }
312         } else if (!wrap) {
313             ClassUtil.throwIfRTE(t);
314         }
315         // Need to add reference information
316         throw JsonMappingException.wrapWithPath(t, bean, fieldName);
317     }
318 
wrapAndThrow(SerializerProvider provider, Throwable t, Object bean, int index)319     public void wrapAndThrow(SerializerProvider provider,
320             Throwable t, Object bean, int index)
321         throws IOException
322     {
323         while (t instanceof InvocationTargetException && t.getCause() != null) {
324             t = t.getCause();
325         }
326         // Errors are to be passed as is
327         ClassUtil.throwIfError(t);
328         // Ditto for IOExceptions... except for mapping exceptions!
329         boolean wrap = (provider == null) || provider.isEnabled(SerializationFeature.WRAP_EXCEPTIONS);
330         if (t instanceof IOException) {
331             if (!wrap || !(t instanceof JsonMappingException)) {
332                 throw (IOException) t;
333             }
334         } else if (!wrap) {
335             ClassUtil.throwIfRTE(t);
336         }
337         // Need to add reference information
338         throw JsonMappingException.wrapWithPath(t, bean, index);
339     }
340 
341     /*
342     /**********************************************************
343     /* Helper methods, accessing annotation-based configuration
344     /**********************************************************
345      */
346 
347     /**
348      * Helper method that can be used to see if specified property has annotation
349      * indicating that a converter is to be used for contained values (contents
350      * of structured types; array/List/Map values)
351      *
352      * @param existingSerializer (optional) configured content
353      *    serializer if one already exists.
354      *
355      * @since 2.9
356      */
findContextualConvertingSerializer(SerializerProvider provider, BeanProperty property, JsonSerializer<?> existingSerializer)357     protected JsonSerializer<?> findContextualConvertingSerializer(SerializerProvider provider,
358             BeanProperty property, JsonSerializer<?> existingSerializer)
359         throws JsonMappingException
360     {
361         // 08-Dec-2016, tatu: to fix [databind#357], need to prevent recursive calls for
362         //     same property
363         @SuppressWarnings("unchecked")
364         Map<Object,Object> conversions = (Map<Object,Object>) provider.getAttribute(KEY_CONTENT_CONVERTER_LOCK);
365         if (conversions != null) {
366             Object lock = conversions.get(property);
367             if (lock != null) {
368                 return existingSerializer;
369             }
370         } else {
371             conversions = new IdentityHashMap<>();
372             provider.setAttribute(KEY_CONTENT_CONVERTER_LOCK, conversions);
373         }
374         conversions.put(property, Boolean.TRUE);
375         try {
376             JsonSerializer<?> ser = findConvertingContentSerializer(provider, property, existingSerializer);
377             if (ser != null) {
378                 return provider.handleSecondaryContextualization(ser, property);
379             }
380         } finally {
381             conversions.remove(property);
382         }
383         return existingSerializer;
384     }
385 
386     /**
387      * @deprecated Since 2.9 use {link {@link #findContextualConvertingSerializer} instead
388      */
389     @Deprecated
findConvertingContentSerializer(SerializerProvider provider, BeanProperty prop, JsonSerializer<?> existingSerializer)390     protected JsonSerializer<?> findConvertingContentSerializer(SerializerProvider provider,
391             BeanProperty prop, JsonSerializer<?> existingSerializer)
392         throws JsonMappingException
393     {
394         final AnnotationIntrospector intr = provider.getAnnotationIntrospector();
395         if (_neitherNull(intr, prop)) {
396             AnnotatedMember m = prop.getMember();
397             if (m != null) {
398                 Object convDef = intr.findSerializationContentConverter(m);
399                 if (convDef != null) {
400                     Converter<Object,Object> conv = provider.converterInstance(prop.getMember(), convDef);
401                     JavaType delegateType = conv.getOutputType(provider.getTypeFactory());
402                     // [databind#731]: Should skip if nominally java.lang.Object
403                     if ((existingSerializer == null) && !delegateType.isJavaLangObject()) {
404                         existingSerializer = provider.findValueSerializer(delegateType);
405                     }
406                     return new StdDelegatingSerializer(conv, delegateType, existingSerializer);
407                 }
408             }
409         }
410         return existingSerializer;
411     }
412 
413     /**
414      * Helper method used to locate filter that is needed, based on filter id
415      * this serializer was constructed with.
416      *
417      * @since 2.3
418      */
findPropertyFilter(SerializerProvider provider, Object filterId, Object valueToFilter)419     protected PropertyFilter findPropertyFilter(SerializerProvider provider,
420             Object filterId, Object valueToFilter)
421         throws JsonMappingException
422     {
423         FilterProvider filters = provider.getFilterProvider();
424         // Not ok to miss the provider, if a filter is declared to be needed.
425         if (filters == null) {
426             provider.reportBadDefinition(handledType(),
427                     "Cannot resolve PropertyFilter with id '"+filterId+"'; no FilterProvider configured");
428         }
429         // But whether unknown ids are ok just depends on filter provider; if we get null that's fine
430         return filters.findPropertyFilter(filterId, valueToFilter);
431     }
432 
433     /**
434      * Helper method that may be used to find if this deserializer has specific
435      * {@link JsonFormat} settings, either via property, or through type-specific
436      * defaulting.
437      *
438      * @param typeForDefaults Type (erased) used for finding default format settings, if any
439      *
440      * @since 2.7
441      */
findFormatOverrides(SerializerProvider provider, BeanProperty prop, Class<?> typeForDefaults)442     protected JsonFormat.Value findFormatOverrides(SerializerProvider provider,
443             BeanProperty prop, Class<?> typeForDefaults)
444     {
445         if (prop != null) {
446             return prop.findPropertyFormat(provider.getConfig(), typeForDefaults);
447         }
448         // even without property or AnnotationIntrospector, may have type-specific defaults
449         return provider.getDefaultPropertyFormat(typeForDefaults);
450     }
451 
452     /**
453      * Convenience method that uses {@link #findFormatOverrides} to find possible
454      * defaults and/of overrides, and then calls <code>JsonFormat.Value.getFeature(...)</code>
455      * to find whether that feature has been specifically marked as enabled or disabled.
456      *
457      * @param typeForDefaults Type (erased) used for finding default format settings, if any
458      *
459      * @since 2.7
460      */
findFormatFeature(SerializerProvider provider, BeanProperty prop, Class<?> typeForDefaults, JsonFormat.Feature feat)461     protected Boolean findFormatFeature(SerializerProvider provider,
462             BeanProperty prop, Class<?> typeForDefaults, JsonFormat.Feature feat)
463     {
464         JsonFormat.Value format = findFormatOverrides(provider, prop, typeForDefaults);
465         if (format != null) {
466             return format.getFeature(feat);
467         }
468         return null;
469     }
470 
471     /**
472      * @since 2.8
473      */
findIncludeOverrides(SerializerProvider provider, BeanProperty prop, Class<?> typeForDefaults)474     protected JsonInclude.Value findIncludeOverrides(SerializerProvider provider,
475             BeanProperty prop, Class<?> typeForDefaults)
476     {
477         if (prop != null) {
478             return prop.findPropertyInclusion(provider.getConfig(), typeForDefaults);
479         }
480         // even without property or AnnotationIntrospector, may have type-specific defaults
481         return provider.getDefaultPropertyInclusion(typeForDefaults);
482     }
483 
484     /**
485      * Convenience method for finding out possibly configured content value serializer.
486      *
487      * @since 2.7.4
488      */
findAnnotatedContentSerializer(SerializerProvider serializers, BeanProperty property)489     protected JsonSerializer<?> findAnnotatedContentSerializer(SerializerProvider serializers,
490             BeanProperty property)
491         throws JsonMappingException
492     {
493         if (property != null) {
494             // First: if we have a property, may have property-annotation overrides
495             AnnotatedMember m = property.getMember();
496             final AnnotationIntrospector intr = serializers.getAnnotationIntrospector();
497             if (m != null) {
498                 Object serDef = intr.findContentSerializer(m);
499                 if (serDef != null) {
500                     return serializers.serializerInstance(m, serDef);
501                 }
502             }
503         }
504         return null;
505     }
506 
507     /*
508     /**********************************************************
509     /* Helper methods, other
510     /**********************************************************
511      */
512 
513     /**
514      * Method that can be called to determine if given serializer is the default
515      * serializer Jackson uses; as opposed to a custom serializer installed by
516      * a module or calling application. Determination is done using
517      * {@link JacksonStdImpl} annotation on serializer class.
518      */
isDefaultSerializer(JsonSerializer<?> serializer)519     protected boolean isDefaultSerializer(JsonSerializer<?> serializer) {
520         return ClassUtil.isJacksonStdImpl(serializer);
521     }
522 
523     /**
524      * @since 2.9
525      */
_neitherNull(Object a, Object b)526     protected final static boolean _neitherNull(Object a, Object b) {
527         return (a != null) && (b != null);
528     }
529 
530     /**
531      * @since 2.9
532      */
_nonEmpty(Collection<?> c)533     protected final static boolean _nonEmpty(Collection<?> c) {
534         return (c != null) && !c.isEmpty();
535     }
536 }
537