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.LinkedHashSet;
7 import java.util.Set;
8
9 import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
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.introspect.AnnotatedMember;
15 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitable;
16 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
17 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonStringFormatVisitor;
18 import com.fasterxml.jackson.databind.jsonschema.SchemaAware;
19 import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
20 import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
21 import com.fasterxml.jackson.databind.ser.BeanSerializer;
22 import com.fasterxml.jackson.databind.ser.ContextualSerializer;
23 import com.fasterxml.jackson.databind.util.ClassUtil;
24
25 /**
26  * Serializer class that can serialize Object that have a
27  * {@link com.fasterxml.jackson.annotation.JsonValue} annotation to
28  * indicate that serialization should be done by calling the method
29  * annotated, and serializing result it returns.
30  *<p>
31  * Implementation note: we will post-process resulting serializer
32  * (much like what is done with {@link BeanSerializer})
33  * to figure out actual serializers for final types.
34  *  This must be done from {@link #createContextual} method, and NOT from constructor;
35  * otherwise we could end up with an infinite loop.
36  */

37 @SuppressWarnings("serial")
38 @JacksonStdImpl
39 public class JsonValueSerializer
40     extends StdSerializer<Object>
41     implements ContextualSerializer, JsonFormatVisitable, SchemaAware
42 {
43     /**
44      * @since 2.9
45      */

46     protected final AnnotatedMember _accessor;
47
48     protected final JsonSerializer<Object> _valueSerializer;
49
50     protected final BeanProperty _property;
51
52     /**
53      * This is a flag that is set in rare (?) cases where this serializer
54      * is used for "natural" types (booleanint, String, double); and where
55      * we actually must force type information wrapping, even though
56      * one would not normally be added.
57      */

58     protected final boolean _forceTypeInformation;
59     
60     /*
61     /**********************************************************
62     /* Life-cycle
63     /**********************************************************
64      */

65
66     /**
67      * @param ser Explicit serializer to use, if caller knows it (which
68      *    occurs if and only if the "value method" was annotated with
69      *    {@link com.fasterxml.jackson.databind.annotation.JsonSerialize#using}), otherwise
70      *    null
71      *    
72      * @since 2.8 Earlier method took "raw" Method, but that does not work with access
73      *    to information we need
74      */

75     @SuppressWarnings("unchecked")
76     public JsonValueSerializer(AnnotatedMember accessor, JsonSerializer<?> ser)
77     {
78         super(accessor.getType());
79         _accessor = accessor;
80         _valueSerializer = (JsonSerializer<Object>) ser;
81         _property = null;
82         _forceTypeInformation = true// gets reconsidered when we are contextualized
83     }
84
85     @SuppressWarnings("unchecked")
86     public JsonValueSerializer(JsonValueSerializer src, BeanProperty property,
87             JsonSerializer<?> ser, boolean forceTypeInfo)
88     {
89         super(_notNullClass(src.handledType()));
90         _accessor = src._accessor;
91         _valueSerializer = (JsonSerializer<Object>) ser;
92         _property = property;
93         _forceTypeInformation = forceTypeInfo;
94     }
95
96     @SuppressWarnings("unchecked")
97     private final static Class<Object> _notNullClass(Class<?> cls) {
98         return (cls == null) ? Object.class : (Class<Object>) cls;
99     }
100
101     public JsonValueSerializer withResolved(BeanProperty property,
102             JsonSerializer<?> ser, boolean forceTypeInfo)
103     {
104         if (_property == property && _valueSerializer == ser
105                 && forceTypeInfo == _forceTypeInformation) {
106             return this;
107         }
108         return new JsonValueSerializer(this, property, ser, forceTypeInfo);
109     }
110     
111     /*
112     /**********************************************************
113     /* Post-processing
114     /**********************************************************
115      */

116
117     /**
118      * We can try to find the actual serializer for value, if we can
119      * statically figure out what the result type must be.
120      */

121     @Override
122     public JsonSerializer<?> createContextual(SerializerProvider provider,
123             BeanProperty property)
124         throws JsonMappingException
125     {
126         JsonSerializer<?> ser = _valueSerializer;
127         if (ser == null) {
128             // Can only assign serializer statically if the declared type is final:
129             // if not, we don't really know the actual type until we get the instance.
130
131             // 10-Mar-2010, tatu: Except if static typing is to be used
132             JavaType t = _accessor.getType();
133             if (provider.isEnabled(MapperFeature.USE_STATIC_TYPING) || t.isFinal()) {
134                 // false -> no need to cache
135                 /* 10-Mar-2010, tatu: Ideally we would actually separate out type
136                  *   serializer from value serializer; but, alas, there's no access
137                  *   to serializer factory at this point... 
138                  */

139                 // 05-Sep-2013, tatu: I _think_ this can be considered a primary property...
140                 ser = provider.findPrimaryPropertySerializer(t, property);
141                 /* 09-Dec-2010, tatu: Turns out we must add special handling for
142                  *   cases where "native" (aka "natural") type is being serialized,
143                  *   using standard serializer
144                  */

145                 boolean forceTypeInformation = isNaturalTypeWithStdHandling(t.getRawClass(), ser);
146                 return withResolved(property, ser, forceTypeInformation);
147             }
148             // [databind#2822]: better hold on to "property", regardless
149             if (property != _property) {
150                 return withResolved(property, ser, _forceTypeInformation);
151             }
152         } else {
153             // 05-Sep-2013, tatu: I _think_ this can be considered a primary property...
154             ser = provider.handlePrimaryContextualization(ser, property);
155             return withResolved(property, ser, _forceTypeInformation);
156         }
157         return this;
158     }
159
160     /*
161     /**********************************************************
162     /* Actual serialization
163     /**********************************************************
164      */

165     
166     @Override
167     public void serialize(Object bean, JsonGenerator gen, SerializerProvider prov) throws IOException
168     {
169         try {
170             Object value = _accessor.getValue(bean);
171             if (value == null) {
172                 prov.defaultSerializeNull(gen);
173                 return;
174             }
175             JsonSerializer<Object> ser = _valueSerializer;
176             if (ser == null) {
177                 Class<?> c = value.getClass();
178                 // 10-Mar-2010, tatu: Ideally we would actually separate out type
179                 //   serializer from value serializer; but, alas, there's no access
180                 //   to serializer factory at this point...
181
182                 // let's cache it, may be needed soon again
183                 ser = prov.findTypedValueSerializer(c, true, _property);
184             }
185             ser.serialize(value, gen, prov);
186         } catch (Exception e) {
187             wrapAndThrow(prov, e, bean, _accessor.getName() + "()");
188         }
189     }
190
191     @Override
192     public void serializeWithType(Object bean, JsonGenerator gen, SerializerProvider provider,
193             TypeSerializer typeSer0) throws IOException
194     {
195         // Regardless of other parts, first need to find value to serialize:
196         Object value = null;
197         try {
198             value = _accessor.getValue(bean);
199             // and if we got null, can also just write it directly
200             if (value == null) {
201                 provider.defaultSerializeNull(gen);
202                 return;
203             }
204             JsonSerializer<Object> ser = _valueSerializer;
205             if (ser == null) { // no serializer yet? Need to fetch
206                 ser = provider.findValueSerializer(value.getClass(), _property);
207             } else {
208                 // 09-Dec-2010, tatu: To work around natural type's refusal to add type info, we do
209                 //    this (note: type is for the wrapper type, not enclosed value!)
210                 if (_forceTypeInformation) {
211                     // Confusing? Type id is for POJO and NOT for value returned by JsonValue accessor...
212                     WritableTypeId typeIdDef = typeSer0.writeTypePrefix(gen,
213                             typeSer0.typeId(bean, JsonToken.VALUE_STRING));
214                     ser.serialize(value, gen, provider);
215                     typeSer0.writeTypeSuffix(gen, typeIdDef);
216
217                     return;
218                 }
219             }
220             // 28-Sep-2016, tatu: As per [databind#1385], we do need to do some juggling
221             //    to use different Object for type id (logical type) and actual serialization
222             //    (delegat type).
223             TypeSerializerRerouter rr = new TypeSerializerRerouter(typeSer0, bean);
224             ser.serializeWithType(value, gen, provider, rr);
225         } catch (Exception e) {
226             wrapAndThrow(provider, e, bean, _accessor.getName() + "()");
227         }
228     }
229     
230     @SuppressWarnings("deprecation")
231     @Override
232     public JsonNode getSchema(SerializerProvider provider, Type typeHint)
233         throws JsonMappingException
234     {
235         if (_valueSerializer instanceof SchemaAware) {
236             return ((SchemaAware)_valueSerializer).getSchema(provider, null);
237         }
238         return com.fasterxml.jackson.databind.jsonschema.JsonSchema.getDefaultSchemaNode();
239     }
240
241     @Override
242     public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
243         throws JsonMappingException
244     {
245         /* 27-Apr-2015, tatu: First things first; for JSON Schema introspection,
246          *    Enum types that use `@JsonValue` are special (but NOT necessarily
247          *    anything else that RETURNS an enum!)
248          *    So we will need to add special
249          *    handling here (see https://github.com/FasterXML/jackson-module-jsonSchema/issues/57
250          *    for details).
251          *    
252          *    Note that meaning of JsonValue, then, is very different for Enums. Sigh.
253          */

254         final JavaType type = _accessor.getType();
255         Class<?> declaring = _accessor.getDeclaringClass();
256         if ((declaring != null) && ClassUtil.isEnumType(declaring)) {
257             if (_acceptJsonFormatVisitorForEnum(visitor, typeHint, declaring)) {
258                 return;
259             }
260         }
261         JsonSerializer<Object> ser = _valueSerializer;
262         if (ser == null) {
263             ser = visitor.getProvider().findTypedValueSerializer(type, false, _property);
264             if (ser == null) { // can this ever occur?
265                 visitor.expectAnyFormat(typeHint);
266                 return;
267             }
268         }
269         ser.acceptJsonFormatVisitor(visitor, type);
270     }
271
272     /**
273      * Overridable helper method used for special case handling of schema information for
274      * Enums.
275      * 
276      * @return True if method handled callbacks; false if not; in latter case caller will
277      *   send default callbacks
278      *
279      * @since 2.6
280      */

281     protected boolean _acceptJsonFormatVisitorForEnum(JsonFormatVisitorWrapper visitor,
282             JavaType typeHint, Class<?> enumType)
283         throws JsonMappingException
284     {
285         // Copied from EnumSerializer#acceptJsonFormatVisitor
286         JsonStringFormatVisitor stringVisitor = visitor.expectStringFormat(typeHint);
287         if (stringVisitor != null) {
288             Set<String> enums = new LinkedHashSet<String>();
289             for (Object en : enumType.getEnumConstants()) {
290                 try {
291                     // 21-Apr-2016, tatu: This is convoluted to the max, but essentially we
292                     //   call `@JsonValue`-annotated accessor method on all Enum members,
293                     //   so it all "works out". To some degree.
294                     enums.add(String.valueOf(_accessor.getValue(en)));
295                 } catch (Exception e) {
296                     Throwable t = e;
297                     while (t instanceof InvocationTargetException && t.getCause() != null) {
298                         t = t.getCause();
299                     }
300                     ClassUtil.throwIfError(t);
301                     throw JsonMappingException.wrapWithPath(t, en, _accessor.getName() + "()");
302                 }
303             }
304             stringVisitor.enumTypes(enums);
305         }
306         return true;
307     }
308
309     protected boolean isNaturalTypeWithStdHandling(Class<?> rawType, JsonSerializer<?> ser)
310     {
311         // First: do we have a natural type being handled?
312         if (rawType.isPrimitive()) {
313             if (rawType != Integer.TYPE && rawType != Boolean.TYPE && rawType != Double.TYPE) {
314                 return false;
315             }
316         } else {
317             if (rawType != String.class &&
318                     rawType != Integer.class && rawType != Boolean.class && rawType != Double.class) {
319                 return false;
320             }
321         }
322         return isDefaultSerializer(ser);
323     }
324
325     /*
326     /**********************************************************
327     /* Other methods
328     /**********************************************************
329      */

330
331     @Override
332     public String toString() {
333         return "(@JsonValue serializer for method " + _accessor.getDeclaringClass() + "#" + _accessor.getName() + ")";
334     }
335
336     /*
337     /**********************************************************
338     /* Helper class
339     /**********************************************************
340      */

341
342     /**
343      * Silly little wrapper class we need to re-route type serialization so that we can
344      * override Object to use for type id (logical type) even when asking serialization
345      * of something else (delegate type)
346      */

347     static class TypeSerializerRerouter
348         extends TypeSerializer
349     {
350         protected final TypeSerializer _typeSerializer;
351         protected final Object _forObject;
352
353         public TypeSerializerRerouter(TypeSerializer ts, Object ob) {
354             _typeSerializer = ts;
355             _forObject = ob;
356         }
357
358         @Override
359         public TypeSerializer forProperty(BeanProperty prop) { // should never get called
360             throw new UnsupportedOperationException();
361         }
362
363         @Override
364         public As getTypeInclusion() {
365             return _typeSerializer.getTypeInclusion();
366         }
367
368         @Override
369         public String getPropertyName() {
370             return _typeSerializer.getPropertyName();
371         }
372
373         @Override
374         public TypeIdResolver getTypeIdResolver() {
375             return _typeSerializer.getTypeIdResolver();
376         }
377
378         // // // New Write API, 2.9+
379         
380         @Override // since 2.9
381         public WritableTypeId writeTypePrefix(JsonGenerator g,
382                 WritableTypeId typeId) throws IOException {
383             // 28-Jun-2017, tatu: Important! Need to "override" value
384             typeId.forValue = _forObject;
385             return _typeSerializer.writeTypePrefix(g, typeId);
386         }
387
388         @Override // since 2.9
389         public WritableTypeId writeTypeSuffix(JsonGenerator g,
390                 WritableTypeId typeId) throws IOException {
391             // NOTE: already overwrote value object so:
392             return _typeSerializer.writeTypeSuffix(g, typeId);
393         }
394
395         // // // Old Write API, pre-2.9
396
397         @Override
398         @Deprecated
399         public void writeTypePrefixForScalar(Object value, JsonGenerator gen) throws IOException {
400             _typeSerializer.writeTypePrefixForScalar(_forObject, gen);
401         }
402
403         @Override
404         @Deprecated
405         public void writeTypePrefixForObject(Object value, JsonGenerator gen) throws IOException {
406             _typeSerializer.writeTypePrefixForObject(_forObject, gen);
407         }
408
409         @Override
410         @Deprecated
411         public void writeTypePrefixForArray(Object value, JsonGenerator gen) throws IOException {
412             _typeSerializer.writeTypePrefixForArray(_forObject, gen);
413         }
414
415         @Override
416         @Deprecated
417         public void writeTypeSuffixForScalar(Object value, JsonGenerator gen) throws IOException {
418             _typeSerializer.writeTypeSuffixForScalar(_forObject, gen);
419         }
420
421         @Override
422         @Deprecated
423         public void writeTypeSuffixForObject(Object value, JsonGenerator gen) throws IOException {
424             _typeSerializer.writeTypeSuffixForObject(_forObject, gen);
425         }
426
427         @Override
428         @Deprecated
429         public void writeTypeSuffixForArray(Object value, JsonGenerator gen) throws IOException {
430             _typeSerializer.writeTypeSuffixForArray(_forObject, gen);
431         }
432
433         @Override
434         @Deprecated
435         public void writeTypePrefixForScalar(Object value, JsonGenerator gen, Class<?> type) throws IOException {
436             _typeSerializer.writeTypePrefixForScalar(_forObject, gen, type);
437         }
438
439         @Override
440         @Deprecated
441         public void writeTypePrefixForObject(Object value, JsonGenerator gen, Class<?> type) throws IOException {
442             _typeSerializer.writeTypePrefixForObject(_forObject, gen, type);
443         }
444
445         @Override
446         @Deprecated
447         public void writeTypePrefixForArray(Object value, JsonGenerator gen, Class<?> type) throws IOException {
448             _typeSerializer.writeTypePrefixForArray(_forObject, gen, type);
449         }
450
451         /*
452         /**********************************************************
453         /* Deprecated methods (since 2.9)
454         /**********************************************************
455          */

456
457         @Override
458         @Deprecated
459         public void writeCustomTypePrefixForScalar(Object value, JsonGenerator gen, String typeId)
460                 throws IOException {
461             _typeSerializer.writeCustomTypePrefixForScalar(_forObject, gen, typeId);
462         }
463
464         @Override
465         @Deprecated
466         public void writeCustomTypePrefixForObject(Object value, JsonGenerator gen, String typeId) throws IOException {
467             _typeSerializer.writeCustomTypePrefixForObject(_forObject, gen, typeId);
468         }
469
470         @Override
471         @Deprecated
472         public void writeCustomTypePrefixForArray(Object value, JsonGenerator gen, String typeId) throws IOException {
473             _typeSerializer.writeCustomTypePrefixForArray(_forObject, gen, typeId);
474         }
475
476         @Override
477         @Deprecated
478         public void writeCustomTypeSuffixForScalar(Object value, JsonGenerator gen, String typeId) throws IOException {
479             _typeSerializer.writeCustomTypeSuffixForScalar(_forObject, gen, typeId);
480         }
481
482         @Override
483         @Deprecated
484         public void writeCustomTypeSuffixForObject(Object value, JsonGenerator gen, String typeId) throws IOException {
485             _typeSerializer.writeCustomTypeSuffixForObject(_forObject, gen, typeId);
486         }
487
488         @Override
489         @Deprecated
490         public void writeCustomTypeSuffixForArray(Object value, JsonGenerator gen, String typeId) throws IOException {
491             _typeSerializer.writeCustomTypeSuffixForArray(_forObject, gen, typeId);
492         }
493     }
494 }
495