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 null, null 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