1 package com.fasterxml.jackson.databind.ser;
2
3 import java.io.IOException;
4 import java.lang.annotation.Annotation;
5 import java.lang.reflect.Field;
6 import java.lang.reflect.Method;
7 import java.lang.reflect.Type;
8 import java.util.HashMap;
9
10 import com.fasterxml.jackson.annotation.JsonInclude;
11 import com.fasterxml.jackson.core.JsonGenerator;
12 import com.fasterxml.jackson.core.SerializableString;
13 import com.fasterxml.jackson.core.io.SerializedString;
14 import com.fasterxml.jackson.databind.*;
15 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
16 import com.fasterxml.jackson.databind.introspect.*;
17 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor;
18 import com.fasterxml.jackson.databind.jsonschema.SchemaAware;
19 import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
20 import com.fasterxml.jackson.databind.node.ObjectNode;
21 import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap;
22 import com.fasterxml.jackson.databind.ser.impl.UnwrappingBeanPropertyWriter;
23 import com.fasterxml.jackson.databind.ser.std.BeanSerializerBase;
24 import com.fasterxml.jackson.databind.util.Annotations;
25 import com.fasterxml.jackson.databind.util.ClassUtil;
26 import com.fasterxml.jackson.databind.util.NameTransformer;
27
28 /**
29  * Base bean property handler class, which implements common parts of
30  * reflection-based functionality for accessing a property value and serializing
31  * it.
32  * <p>
33  * Note that current design tries to keep instances immutable (semi-functional
34  * style); mostly because these instances are exposed to application code and
35  * this is to reduce likelihood of data corruption and synchronization issues.
36  */

37 @JacksonStdImpl
38 // since 2.6. NOTE: sub-classes typically are not
39 public class BeanPropertyWriter extends PropertyWriter // which extends
40                                                        // `ConcreteBeanPropertyBase`
41         implements java.io.Serializable // since 2.6
42 {
43     // As of 2.7
44     private static final long serialVersionUID = 1L;
45
46     /**
47      * Marker object used to indicate "do not serialize if empty"
48      */

49     public final static Object MARKER_FOR_EMPTY = JsonInclude.Include.NON_EMPTY;
50
51     /*
52     /***********************************************************
53     /* Basic property metadata: name, type, other
54     /***********************************************************
55      */

56
57     /**
58      * Logical name of the property; will be used as the field name under which
59      * value for the property is written.
60      * <p>
61      * NOTE: do NOT change name of this field; it is accessed by Afterburner
62      * module (until 2.4; not directly from 2.5) ALSO NOTE: ... and while it
63      * really ought to be `SerializableString`, changing that is also
64      * binary-incompatible change. So nope.
65      */

66     protected final SerializedString _name;
67
68     /**
69      * Wrapper name to use for this element, if any
70      * 
71      * @since 2.2
72      */

73     protected final PropertyName _wrapperName;
74
75     /**
76      * Type property is declared to have, either in class definition or
77      * associated annotations.
78      */

79     protected final JavaType _declaredType;
80
81     /**
82      * Type to use for locating serializer; normally same as return type of the
83      * accessor method, but may be overridden by annotations.
84      */

85     protected final JavaType _cfgSerializationType;
86
87     /**
88      * Base type of the property, if the declared type is "non-trivial"; meaning
89      * it is either a structured type (collection, map, array), or
90      * parameterized. Used to retain type information about contained type,
91      * which is mostly necessary if type meta-data is to be included.
92      */

93     protected JavaType _nonTrivialBaseType;
94
95     /**
96      * Annotations from context (most often, class that declares property, or in
97      * case of sub-class serializer, from that sub-class)
98      * <p>
99      * NOTE: transient just to support JDK serializability; Annotations do not
100      * serialize. At all.
101      */

102     protected final transient Annotations _contextAnnotations;
103
104     /*
105     /***********************************************************
106     /* Settings for accessing property value to serialize
107     /***********************************************************
108      */

109
110     /**
111      * Member (field, method) that represents property and allows access to
112      * associated annotations.
113      */

114     protected final AnnotatedMember _member;
115
116     /**
117      * Accessor method used to get property value, for method-accessible
118      * properties. Null if and only if {@link #_field} is null.
119      * <p>
120      * `transient` (and non-final) only to support JDK serializability.
121      */

122     protected transient Method _accessorMethod;
123
124     /**
125      * Field that contains the property value for field-accessible properties.
126      * Null if and only if {@link #_accessorMethod} is null.
127      * <p>
128      * `transient` (and non-final) only to support JDK serializability.
129      */

130     protected transient Field _field;
131
132     /*
133     /***********************************************************
134     /* Serializers needed
135     /***********************************************************
136      */

137
138     /**
139      * Serializer to use for writing out the value: null if it cannot be known
140      * statically; non-null if it can.
141      */

142     protected JsonSerializer<Object> _serializer;
143
144     /**
145      * Serializer used for writing out null values, if any: if nullnull values
146      * are to be suppressed.
147      */

148     protected JsonSerializer<Object> _nullSerializer;
149
150     /**
151      * If property being serialized needs type information to be included this
152      * is the type serializer to use. Declared type (possibly augmented with
153      * annotations) of property is used for determining exact mechanism to use
154      * (compared to actual runtime type used for serializing actual state).
155      */

156     protected TypeSerializer _typeSerializer;
157
158     /**
159      * In case serializer is not known statically (i.e. <code>_serializer</code>
160      * is null), we will use a lookup structure for storing dynamically resolved
161      * mapping from type(s) to serializer(s).
162      */

163     protected transient PropertySerializerMap _dynamicSerializers;
164
165     /*
166     /***********************************************************
167     /* Filtering
168     /***********************************************************
169      */

170
171     /**
172      * Whether null values are to be suppressed (nothing written out if value is
173      * null) or not. Note that this is a configuration value during
174      * construction, and actual handling relies on setting (or not) of
175      * {@link #_nullSerializer}.
176      */

177     protected final boolean _suppressNulls;
178
179     /**
180      * Value that is considered default value of the property; used for
181      * default-value-suppression if enabled.
182      */

183     protected final Object _suppressableValue;
184
185     /**
186      * Alternate set of property writers used when view-based filtering is
187      * available for the Bean.
188      */

189     protected final Class<?>[] _includeInViews;
190
191     /*
192     /**********************************************************
193     /* Opaqueinternal data that bean serializer factory and
194     /* bean serializers can add.
195     /**********************************************************
196      */

197
198     protected transient HashMap<Object, Object> _internalSettings;
199
200     /*
201     /***********************************************************
202     /* Construction, configuration
203     /***********************************************************
204      */

205
206     /**
207      * @since 2.9 (added `includeInViews` since 2.8)
208      */

209     @SuppressWarnings("unchecked")
210     public BeanPropertyWriter(BeanPropertyDefinition propDef,
211             AnnotatedMember member, Annotations contextAnnotations,
212             JavaType declaredType,
213             JsonSerializer<?> ser, TypeSerializer typeSer, JavaType serType,
214             boolean suppressNulls, Object suppressableValue,
215             Class<?>[] includeInViews)
216     {
217         super(propDef);
218         _member = member;
219         _contextAnnotations = contextAnnotations;
220
221         _name = new SerializedString(propDef.getName());
222         _wrapperName = propDef.getWrapperName();
223
224         _declaredType = declaredType;
225         _serializer = (JsonSerializer<Object>) ser;
226         _dynamicSerializers = (ser == null) ? PropertySerializerMap
227                 .emptyForProperties() : null;
228         _typeSerializer = typeSer;
229         _cfgSerializationType = serType;
230
231         if (member instanceof AnnotatedField) {
232             _accessorMethod = null;
233             _field = (Field) member.getMember();
234         } else if (member instanceof AnnotatedMethod) {
235             _accessorMethod = (Method) member.getMember();
236             _field = null;
237         } else {
238             // 01-Dec-2014, tatu: Used to be illegal, but now explicitly allowed
239             // for virtual props
240             _accessorMethod = null;
241             _field = null;
242         }
243         _suppressNulls = suppressNulls;
244         _suppressableValue = suppressableValue;
245
246         // this will be resolved later on, unless nulls are to be suppressed
247         _nullSerializer = null;
248         _includeInViews = includeInViews;
249     }
250
251     @Deprecated // Since 2.9
252     public BeanPropertyWriter(BeanPropertyDefinition propDef,
253             AnnotatedMember member, Annotations contextAnnotations,
254             JavaType declaredType,
255             JsonSerializer<?> ser, TypeSerializer typeSer, JavaType serType,
256             boolean suppressNulls, Object suppressableValue)
257     {
258         this(propDef, member, contextAnnotations, declaredType,
259                 ser, typeSer, serType, suppressNulls, suppressableValue,
260                 null);
261     }
262
263     /**
264      * Constructor that may be of use to virtual properties, when there is need
265      * for the zero-arg ("default") constructor, and actual initialization is
266      * done after constructor call.
267      * 
268      * @since 2.5
269      */

270     protected BeanPropertyWriter() {
271         super(PropertyMetadata.STD_REQUIRED_OR_OPTIONAL);
272         _member = null;
273         _contextAnnotations = null;
274
275         _name = null;
276         _wrapperName = null;
277         _includeInViews = null;
278
279         _declaredType = null;
280         _serializer = null;
281         _dynamicSerializers = null;
282         _typeSerializer = null;
283         _cfgSerializationType = null;
284
285         _accessorMethod = null;
286         _field = null;
287         _suppressNulls = false;
288         _suppressableValue = null;
289
290         _nullSerializer = null;
291     }
292
293     /**
294      * "Copy constructor" to be used by filtering sub-classes
295      */

296     protected BeanPropertyWriter(BeanPropertyWriter base) {
297         this(base, base._name);
298     }
299
300     /**
301      * @since 2.5
302      */

303     protected BeanPropertyWriter(BeanPropertyWriter base, PropertyName name) {
304         super(base);
305         /*
306          * 02-Dec-2014, tatu: This is a big mess, alas, what with dependency to
307          * MapperConfig to encode, and Afterburner having heartburn for
308          * SerializableString (vs SerializedString). Hope it can be
309          * resolved/reworked in 2.6 timeframe, if not for 2.5
310          */

311         _name = new SerializedString(name.getSimpleName());
312         _wrapperName = base._wrapperName;
313
314         _contextAnnotations = base._contextAnnotations;
315         _declaredType = base._declaredType;
316
317         _member = base._member;
318         _accessorMethod = base._accessorMethod;
319         _field = base._field;
320
321         _serializer = base._serializer;
322         _nullSerializer = base._nullSerializer;
323         // one more thing: copy internal settings, if any
324         if (base._internalSettings != null) {
325             _internalSettings = new HashMap<Object, Object>(
326                     base._internalSettings);
327         }
328         _cfgSerializationType = base._cfgSerializationType;
329         _dynamicSerializers = base._dynamicSerializers;
330         _suppressNulls = base._suppressNulls;
331         _suppressableValue = base._suppressableValue;
332         _includeInViews = base._includeInViews;
333         _typeSerializer = base._typeSerializer;
334         _nonTrivialBaseType = base._nonTrivialBaseType;
335     }
336
337     protected BeanPropertyWriter(BeanPropertyWriter base, SerializedString name) {
338         super(base);
339         _name = name;
340         _wrapperName = base._wrapperName;
341
342         _member = base._member;
343         _contextAnnotations = base._contextAnnotations;
344         _declaredType = base._declaredType;
345         _accessorMethod = base._accessorMethod;
346         _field = base._field;
347         _serializer = base._serializer;
348         _nullSerializer = base._nullSerializer;
349         if (base._internalSettings != null) {
350             _internalSettings = new HashMap<Object, Object>(
351                     base._internalSettings);
352         }
353         _cfgSerializationType = base._cfgSerializationType;
354         _dynamicSerializers = base._dynamicSerializers;
355         _suppressNulls = base._suppressNulls;
356         _suppressableValue = base._suppressableValue;
357         _includeInViews = base._includeInViews;
358         _typeSerializer = base._typeSerializer;
359         _nonTrivialBaseType = base._nonTrivialBaseType;
360     }
361
362     public BeanPropertyWriter rename(NameTransformer transformer) {
363         String newName = transformer.transform(_name.getValue());
364         if (newName.equals(_name.toString())) {
365             return this;
366         }
367         return _new(PropertyName.construct(newName));
368     }
369
370     /**
371      * Overridable factory method used by sub-classes
372      *
373      * @since 2.6
374      */

375     protected BeanPropertyWriter _new(PropertyName newName) {
376         return new BeanPropertyWriter(this, newName);
377     }
378
379     /**
380      * Method called to set, reset or clear the configured type serializer for
381      * property.
382      *
383      * @since 2.6
384      */

385     public void assignTypeSerializer(TypeSerializer typeSer) {
386         _typeSerializer = typeSer;
387     }
388
389     /**
390      * Method called to assign value serializer for property
391      */

392     public void assignSerializer(JsonSerializer<Object> ser) {
393         // may need to disable check in future?
394         if ((_serializer != null) && (_serializer != ser)) {
395             throw new IllegalStateException(String.format(
396                     "Cannot override _serializer: had a %s, trying to set to %s",
397                     ClassUtil.classNameOf(_serializer), ClassUtil.classNameOf(ser)));
398         }
399         _serializer = ser;
400     }
401
402     /**
403      * Method called to assign null value serializer for property
404      */

405     public void assignNullSerializer(JsonSerializer<Object> nullSer) {
406         // may need to disable check in future?
407         if ((_nullSerializer != null) && (_nullSerializer != nullSer)) {
408             throw new IllegalStateException(String.format(
409                     "Cannot override _nullSerializer: had a %s, trying to set to %s",
410                     ClassUtil.classNameOf(_nullSerializer), ClassUtil.classNameOf(nullSer)));
411         }
412         _nullSerializer = nullSer;
413     }
414
415     /**
416      * Method called create an instance that handles details of unwrapping
417      * contained value.
418      */

419     public BeanPropertyWriter unwrappingWriter(NameTransformer unwrapper) {
420         return new UnwrappingBeanPropertyWriter(this, unwrapper);
421     }
422
423     /**
424      * Method called to define type to consider as "non-trivial" basetype,
425      * needed for dynamic serialization resolution for complex (usually
426      * container) types
427      */

428     public void setNonTrivialBaseType(JavaType t) {
429         _nonTrivialBaseType = t;
430     }
431
432     /**
433      * Method called to ensure that the mutator has proper access rights to
434      * be called, as per configuration. Overridden by implementations that
435      * have mutators that require access, fields and setters.
436      *
437      * @since 2.8.3
438      */

439     public void fixAccess(SerializationConfig config) {
440         _member.fixAccess(config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
441     }
442
443     /*
444     /***********************************************************
445     /* JDK Serializability
446     /***********************************************************
447      */

448
449     /*
450      * Ideally would not require mutable state, and instead would re-create with
451      * final settings. However, as things are, with sub-types and all, simplest
452      * to just change Field/Method value directly.
453      */

454     Object readResolve() {
455         if (_member instanceof AnnotatedField) {
456             _accessorMethod = null;
457             _field = (Field) _member.getMember();
458         } else if (_member instanceof AnnotatedMethod) {
459             _accessorMethod = (Method) _member.getMember();
460             _field = null;
461         }
462         if (_serializer == null) {
463             _dynamicSerializers = PropertySerializerMap.emptyForProperties();
464         }
465         return this;
466     }
467
468     /*
469     /************************************************************
470     /* BeanProperty impl
471     /***********************************************************
472      */

473
474     // Note: also part of 'PropertyWriter'
475     @Override
476     public String getName() {
477         return _name.getValue();
478     }
479
480     // Note: also part of 'PropertyWriter'
481     @Override
482     public PropertyName getFullName() { // !!! TODO: impl properly
483         return new PropertyName(_name.getValue());
484     }
485
486     @Override
487     public JavaType getType() {
488         return _declaredType;
489     }
490
491     @Override
492     public PropertyName getWrapperName() {
493         return _wrapperName;
494     }
495
496     // Note: also part of 'PropertyWriter'
497     @Override
498     public <A extends Annotation> A getAnnotation(Class<A> acls) {
499         return (_member == null) ? null : _member.getAnnotation(acls);
500     }
501
502     // Note: also part of 'PropertyWriter'
503     @Override
504     public <A extends Annotation> A getContextAnnotation(Class<A> acls) {
505         return (_contextAnnotations == null) ? null : _contextAnnotations
506                 .get(acls);
507     }
508
509     @Override
510     public AnnotatedMember getMember() {
511         return _member;
512     }
513
514     // @since 2.3 -- needed so it can be overridden by unwrapping writer
515     protected void _depositSchemaProperty(ObjectNode propertiesNode,
516             JsonNode schemaNode) {
517         propertiesNode.set(getName(), schemaNode);
518     }
519
520     /*
521     /***********************************************************
522     /* Managing and accessing of opaque internal settings
523     /* (used by extensions)
524     /***********************************************************
525      */

526
527     /**
528      * Method for accessing value of specified internal setting.
529      * 
530      * @return Value of the setting, if any; null if none.
531      */

532     public Object getInternalSetting(Object key) {
533         return (_internalSettings == null) ? null : _internalSettings.get(key);
534     }
535
536     /**
537      * Method for setting specific internal setting to given value
538      * 
539      * @return Old value of the setting, if any (null if none)
540      */

541     public Object setInternalSetting(Object key, Object value) {
542         if (_internalSettings == null) {
543             _internalSettings = new HashMap<Object, Object>();
544         }
545         return _internalSettings.put(key, value);
546     }
547
548     /**
549      * Method for removing entry for specified internal setting.
550      * 
551      * @return Existing value of the setting, if any (null if none)
552      */

553     public Object removeInternalSetting(Object key) {
554         Object removed = null;
555         if (_internalSettings != null) {
556             removed = _internalSettings.remove(key);
557             // to reduce memory usage, let's also drop the Map itself, if empty
558             if (_internalSettings.size() == 0) {
559                 _internalSettings = null;
560             }
561         }
562         return removed;
563     }
564
565     /*
566     /***********************************************************
567     /* Accessors
568     /***********************************************************
569      */

570
571     public SerializableString getSerializedName() {
572         return _name;
573     }
574
575     public boolean hasSerializer() {
576         return _serializer != null;
577     }
578
579     public boolean hasNullSerializer() {
580         return _nullSerializer != null;
581     }
582
583     /**
584      * @since 2.6
585      */

586     public TypeSerializer getTypeSerializer() {
587         return _typeSerializer;
588     }
589
590     /**
591      * Accessor that will return true if this bean property has to support
592      * "unwrapping"; ability to replace POJO structural wrapping with optional
593      * name prefix and/or suffix (or in some cases, just removal of wrapper
594      * name).
595      * <p>
596      * Default implementation simply returns false.
597      * 
598      * @since 2.3
599      */

600     public boolean isUnwrapping() {
601         return false;
602     }
603
604     public boolean willSuppressNulls() {
605         return _suppressNulls;
606     }
607
608     /**
609      * Method called to check to see if this property has a name that would
610      * conflict with a given name.
611      *
612      * @since 2.6
613      */

614     public boolean wouldConflictWithName(PropertyName name) {
615         if (_wrapperName != null) {
616             return _wrapperName.equals(name);
617         }
618         // Bit convoluted since our support for namespaces is spotty but:
619         return name.hasSimpleName(_name.getValue()) && !name.hasNamespace();
620     }
621
622     // Needed by BeanSerializer#getSchema
623     public JsonSerializer<Object> getSerializer() {
624         return _serializer;
625     }
626
627     public JavaType getSerializationType() {
628         return _cfgSerializationType;
629     }
630
631     @Deprecated // since 2.9
632     public Class<?> getRawSerializationType() {
633         return (_cfgSerializationType == null) ? null : _cfgSerializationType
634                 .getRawClass();
635     }
636
637     /**
638      * @deprecated Since 2.7, to be removed from 2.9, use {@link #getType()} instead.
639      */

640     @Deprecated
641     public Class<?> getPropertyType() {
642         if (_accessorMethod != null) {
643             return _accessorMethod.getReturnType();
644         }
645         if (_field != null) {
646             return _field.getType();
647         }
648         return null;
649     }
650
651     /**
652      * Get the generic property type of this property writer.
653      *
654      * @return The property type, or null if not found.
655      *
656      * @deprecated Since 2.7, to be removed from 2.9, use {@link #getType()} instead.
657      */

658     @Deprecated
659     public Type getGenericPropertyType() {
660         if (_accessorMethod != null) {
661             return _accessorMethod.getGenericReturnType();
662         }
663         if (_field != null) {
664             return _field.getGenericType();
665         }
666         return null;
667     }
668
669     public Class<?>[] getViews() {
670         return _includeInViews;
671     }
672
673     /*
674     /***********************************************************
675     /* PropertyWriter methods (serialization)
676     /***********************************************************
677      */

678
679     /**
680      * Method called to access property that this bean stands for, from within
681      * given bean, and to serialize it as a JSON Object field using appropriate
682      * serializer.
683      */

684     @Override
685     public void serializeAsField(Object bean, JsonGenerator gen,
686             SerializerProvider prov) throws Exception {
687         // inlined 'get()'
688         final Object value = (_accessorMethod == null) ? _field.get(bean)
689                 : _accessorMethod.invoke(bean, (Object[]) null);
690
691         // Null handling is bit different, check that first
692         if (value == null) {
693             if (_nullSerializer != null) {
694                 gen.writeFieldName(_name);
695                 _nullSerializer.serialize(null, gen, prov);
696             }
697             return;
698         }
699         // then find serializer to use
700         JsonSerializer<Object> ser = _serializer;
701         if (ser == null) {
702             Class<?> cls = value.getClass();
703             PropertySerializerMap m = _dynamicSerializers;
704             ser = m.serializerFor(cls);
705             if (ser == null) {
706                 ser = _findAndAddDynamic(m, cls, prov);
707             }
708         }
709         // and then see if we must suppress certain values (default, empty)
710         if (_suppressableValue != null) {
711             if (MARKER_FOR_EMPTY == _suppressableValue) {
712                 if (ser.isEmpty(prov, value)) {
713                     return;
714                 }
715             } else if (_suppressableValue.equals(value)) {
716                 return;
717             }
718         }
719         // For non-nulls: simple check for direct cycles
720         if (value == bean) {
721             // four choices: exception; handled by call; pass-through or write null
722             if (_handleSelfReference(bean, gen, prov, ser)) {
723                 return;
724             }
725         }
726         gen.writeFieldName(_name);
727         if (_typeSerializer == null) {
728             ser.serialize(value, gen, prov);
729         } else {
730             ser.serializeWithType(value, gen, prov, _typeSerializer);
731         }
732     }
733
734     /**
735      * Method called to indicate that serialization of a field was omitted due
736      * to filtering, in cases where backend data format does not allow basic
737      * omission.
738      * 
739      * @since 2.3
740      */

741     @Override
742     public void serializeAsOmittedField(Object bean, JsonGenerator gen,
743             SerializerProvider prov) throws Exception {
744         if (!gen.canOmitFields()) {
745             gen.writeOmittedField(_name.getValue());
746         }
747     }
748
749     /**
750      * Alternative to {@link #serializeAsField} that is used when a POJO is
751      * serialized as JSON Array; the difference is that no field names are
752      * written.
753      * 
754      * @since 2.3
755      */

756     @Override
757     public void serializeAsElement(Object bean, JsonGenerator gen,
758             SerializerProvider prov) throws Exception {
759         // inlined 'get()'
760         final Object value = (_accessorMethod == null) ? _field.get(bean)
761                 : _accessorMethod.invoke(bean, (Object[]) null);
762         if (value == null) { // nulls need specialized handling
763             if (_nullSerializer != null) {
764                 _nullSerializer.serialize(null, gen, prov);
765             } else { // can NOT suppress entries in tabular output
766                 gen.writeNull();
767             }
768             return;
769         }
770         // otherwise find serializer to use
771         JsonSerializer<Object> ser = _serializer;
772         if (ser == null) {
773             Class<?> cls = value.getClass();
774             PropertySerializerMap map = _dynamicSerializers;
775             ser = map.serializerFor(cls);
776             if (ser == null) {
777                 ser = _findAndAddDynamic(map, cls, prov);
778             }
779         }
780         // and then see if we must suppress certain values (default, empty)
781         if (_suppressableValue != null) {
782             if (MARKER_FOR_EMPTY == _suppressableValue) {
783                 if (ser.isEmpty(prov, value)) { // can NOT suppress entries in
784                                                 // tabular output
785                     serializeAsPlaceholder(bean, gen, prov);
786                     return;
787                 }
788             } else if (_suppressableValue.equals(value)) { // can NOT suppress
789                                                            // entries in tabular
790                                                            // output
791                 serializeAsPlaceholder(bean, gen, prov);
792                 return;
793             }
794         }
795         // For non-nulls: simple check for direct cycles
796         if (value == bean) {
797             if (_handleSelfReference(bean, gen, prov, ser)) {
798                 return;
799             }
800         }
801         if (_typeSerializer == null) {
802             ser.serialize(value, gen, prov);
803         } else {
804             ser.serializeWithType(value, gen, prov, _typeSerializer);
805         }
806     }
807
808     /**
809      * Method called to serialize a placeholder used in tabular output when real
810      * value is not to be included (is filtered out), but when we need an entry
811      * so that field indexes will not be off. Typically this should output null
812      * or empty String, depending on datatype.
813      * 
814      * @since 2.1
815      */

816     @Override
817     public void serializeAsPlaceholder(Object bean, JsonGenerator gen,
818             SerializerProvider prov) throws Exception {
819         if (_nullSerializer != null) {
820             _nullSerializer.serialize(null, gen, prov);
821         } else {
822             gen.writeNull();
823         }
824     }
825
826     /*
827     /***********************************************************
828     /* PropertyWriter methods (schema generation)
829     /***********************************************************
830      */

831
832     // Also part of BeanProperty implementation
833     @Override
834     public void depositSchemaProperty(JsonObjectFormatVisitor v,
835             SerializerProvider provider) throws JsonMappingException {
836         if (v != null) {
837             if (isRequired()) {
838                 v.property(this);
839             } else {
840                 v.optionalProperty(this);
841             }
842         }
843     }
844
845     // // // Legacy support for JsonFormatVisitable
846
847     /**
848      * Attempt to add the output of the given {@link BeanPropertyWriter} in the
849      * given {@link ObjectNode}. Otherwise, add the default schema
850      * {@link JsonNode} in place of the writer's output
851      * 
852      * @param propertiesNode
853      *            Node which the given property would exist within
854      * @param provider
855      *            Provider that can be used for accessing dynamic aspects of
856      *            serialization processing
857      */

858     @Override
859     @Deprecated
860     public void depositSchemaProperty(ObjectNode propertiesNode,
861             SerializerProvider provider) throws JsonMappingException {
862         JavaType propType = getSerializationType();
863         // 03-Dec-2010, tatu: SchemaAware REALLY should use JavaType, but alas
864         // it doesn't...
865         Type hint = (propType == null) ? getType() : propType.getRawClass();
866         JsonNode schemaNode;
867         // Maybe it already has annotated/statically configured serializer?
868         JsonSerializer<Object> ser = getSerializer();
869         if (ser == null) { // nope
870             ser = provider.findValueSerializer(getType(), this);
871         }
872         boolean isOptional = !isRequired();
873         if (ser instanceof SchemaAware) {
874             schemaNode = ((SchemaAware) ser).getSchema(provider, hint,
875                     isOptional);
876         } else {
877             schemaNode = com.fasterxml.jackson.databind.jsonschema.JsonSchema
878                     .getDefaultSchemaNode();
879         }
880         _depositSchemaProperty(propertiesNode, schemaNode);
881     }
882
883     /*
884     /**********************************************************
885     /* Helper methods
886     /**********************************************************
887      */

888
889     protected JsonSerializer<Object> _findAndAddDynamic(
890             PropertySerializerMap map, Class<?> type,
891             SerializerProvider provider) throws JsonMappingException {
892         PropertySerializerMap.SerializerAndMapResult result;
893         if (_nonTrivialBaseType != null) {
894             JavaType t = provider.constructSpecializedType(_nonTrivialBaseType,
895                     type);
896             result = map.findAndAddPrimarySerializer(t, provider, this);
897         } else {
898             result = map.findAndAddPrimarySerializer(type, provider, this);
899         }
900         // did we get a new map of serializers? If so, start using it
901         if (map != result.map) {
902             _dynamicSerializers = result.map;
903         }
904         return result.serializer;
905     }
906
907     /**
908      * Method that can be used to access value of the property this Object
909      * describes, from given bean instance.
910      * <p>
911      * Note: method is final as it should not need to be overridden -- rather,
912      * calling method(s) ({@link #serializeAsField}) should be overridden to
913      * change the behavior
914      */

915     public final Object get(Object bean) throws Exception {
916         return (_accessorMethod == null) ? _field.get(bean) : _accessorMethod
917                 .invoke(bean, (Object[]) null);
918     }
919
920     /**
921      * Method called to handle a direct self-reference through this property.
922      * Method can choose to indicate an error by throwing
923      * {@link JsonMappingException}; fully handle serialization (and return
924      * true); or indicate that it should be serialized normally (return false).
925      * <p>
926      * Default implementation will throw {@link JsonMappingException} if
927      * {@link SerializationFeature#FAIL_ON_SELF_REFERENCES} is enabled; or
928      * return <code>false</code> if it is disabled.
929      *
930      * @return True if method fully handled self-referential value; false if not
931      *         (caller is to handle it) or {@link JsonMappingException} if there
932      *         is no way handle it
933      */

934     protected boolean _handleSelfReference(Object bean, JsonGenerator gen,
935             SerializerProvider prov, JsonSerializer<?> ser)
936             throws IOException
937     {
938         if (!ser.usesObjectId()) {
939             if (prov.isEnabled(SerializationFeature.FAIL_ON_SELF_REFERENCES)) {
940                 // 05-Feb-2013, tatu: Usually a problem, but NOT if we are handling
941                 // object id; this may be the case for BeanSerializers at least.
942                 // 13-Feb-2014, tatu: another possible ok case: custom serializer
943                 // (something OTHER than {@link BeanSerializerBase}
944                 if (ser instanceof BeanSerializerBase) {
945                     prov.reportBadDefinition(getType(), "Direct self-reference leading to cycle");
946                 }
947             } else if (prov.isEnabled(SerializationFeature.WRITE_SELF_REFERENCES_AS_NULL)) {
948                 if (_nullSerializer != null) {
949                     // 23-Oct-2019, tatu: Tricky part -- caller does not specify if it's
950                     //   "as property" (in JSON Object) or "as element" (JSON array, via
951                     //   'POJO-as-array'). And since Afterburner calls method can not easily
952                     //   start passing info either. So check generator to see...
953                     //   (note: not considering ROOT context as possibility, does not seem legal)
954                     if (!gen.getOutputContext().inArray()) {
955                         gen.writeFieldName(_name);
956                     }
957                     _nullSerializer.serialize(null, gen, prov);
958                 }
959                 return true;
960             }
961         }
962         return false;
963     }
964
965     @Override
966     public String toString() {
967         StringBuilder sb = new StringBuilder(40);
968         sb.append("property '").append(getName()).append("' (");
969         if (_accessorMethod != null) {
970             sb.append("via method ")
971                     .append(_accessorMethod.getDeclaringClass().getName())
972                     .append("#").append(_accessorMethod.getName());
973         } else if (_field != null) {
974             sb.append("field \"").append(_field.getDeclaringClass().getName())
975                     .append("#").append(_field.getName());
976         } else {
977             sb.append("virtual");
978         }
979         if (_serializer == null) {
980             sb.append(", no static serializer");
981         } else {
982             sb.append(", static serializer of type "
983                     + _serializer.getClass().getName());
984         }
985         sb.append(')');
986         return sb.toString();
987     }
988 }
989