1 package com.fasterxml.jackson.databind.ser.std;
2
3 import java.io.IOException;
4 import java.lang.reflect.Type;
5 import java.util.*;
6
7 import com.fasterxml.jackson.annotation.*;
8
9 import com.fasterxml.jackson.core.*;
10 import com.fasterxml.jackson.core.type.WritableTypeId;
11
12 import com.fasterxml.jackson.databind.*;
13 import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
14 import com.fasterxml.jackson.databind.introspect.ObjectIdInfo;
15 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitable;
16 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
17 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor;
18 import com.fasterxml.jackson.databind.jsonschema.JsonSerializableSchema;
19 import com.fasterxml.jackson.databind.jsonschema.SchemaAware;
20 import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
21 import com.fasterxml.jackson.databind.node.ObjectNode;
22 import com.fasterxml.jackson.databind.ser.*;
23 import com.fasterxml.jackson.databind.ser.impl.MapEntrySerializer;
24 import com.fasterxml.jackson.databind.ser.impl.ObjectIdWriter;
25 import com.fasterxml.jackson.databind.ser.impl.PropertyBasedObjectIdGenerator;
26 import com.fasterxml.jackson.databind.ser.impl.WritableObjectId;
27 import com.fasterxml.jackson.databind.util.ArrayBuilders;
28 import com.fasterxml.jackson.databind.util.Converter;
29 import com.fasterxml.jackson.databind.util.NameTransformer;
30
31 /**
32  * Base class both for the standard bean serializer, and couple
33  * of variants that only differ in small details.
34  * Can be used for custom bean serializers as well, although that
35  * is not the primary design goal.
36  */

37 @SuppressWarnings("serial")
38 public abstract class BeanSerializerBase
39     extends StdSerializer<Object>
40     implements ContextualSerializer, ResolvableSerializer,
41         JsonFormatVisitable, SchemaAware
42 {
43     protected final static PropertyName NAME_FOR_OBJECT_REF = new PropertyName("#object-ref");
44
45     final protected static BeanPropertyWriter[] NO_PROPS = new BeanPropertyWriter[0];
46
47     /*
48     /**********************************************************
49     /* Configuration
50     /**********************************************************
51      */

52
53     /**
54      * @since 2.9
55      */

56     final protected JavaType _beanType;
57
58     /**
59      * Writers used for outputting actual property values
60      */

61     final protected BeanPropertyWriter[] _props;
62
63     /**
64      * Optional filters used to suppress output of properties that
65      * are only to be included in certain views
66      */

67     final protected BeanPropertyWriter[] _filteredProps;
68
69     /**
70      * Handler for {@link com.fasterxml.jackson.annotation.JsonAnyGetter}
71      * annotated properties
72      */

73     final protected AnyGetterWriter _anyGetterWriter;
74
75     /**
76      * Id of the bean property filter to use, if any; null if none.
77      */

78     final protected Object _propertyFilterId;
79
80     /**
81      * If using custom type ids (usually via getter, or field), this is the
82      * reference to that member.
83      */

84     final protected AnnotatedMember _typeId;
85
86     /**
87      * If this POJO can be alternatively serialized using just an object id
88      * to denote a reference to previously serialized object,
89      * this Object will handle details.
90      */

91     final protected ObjectIdWriter _objectIdWriter;
92
93     /**
94      * Requested shape from bean class annotations.
95      */

96     final protected JsonFormat.Shape _serializationShape;
97
98     /*
99     /**********************************************************
100     /* Life-cycle: constructors
101     /**********************************************************
102      */

103
104     /**
105      * Constructor used by {@link BeanSerializerBuilder} to create an
106      * instance
107      * 
108      * @param type Nominal type of values handled by this serializer
109      * @param builder Builder for accessing other collected information
110      */

111     protected BeanSerializerBase(JavaType type, BeanSerializerBuilder builder,
112             BeanPropertyWriter[] properties, BeanPropertyWriter[] filteredProperties)
113     {
114         super(type);
115         _beanType = type;
116         _props = properties;
117         _filteredProps = filteredProperties;
118         if (builder == null) { // mostly for testing
119             // 20-Sep-2019, tatu: Actually not just that but also "dummy" serializer for
120             //     case of no bean properties, too
121             _typeId = null;
122             _anyGetterWriter = null;
123             _propertyFilterId = null;
124             _objectIdWriter = null;
125             _serializationShape = null;
126         } else {
127             _typeId = builder.getTypeId();
128             _anyGetterWriter = builder.getAnyGetter();
129             _propertyFilterId = builder.getFilterId();
130             _objectIdWriter = builder.getObjectIdWriter();
131             JsonFormat.Value format = builder.getBeanDescription().findExpectedFormat(null);
132             _serializationShape = (format == null) ? null : format.getShape();
133         }
134     }
135
136     protected BeanSerializerBase(BeanSerializerBase src,
137             BeanPropertyWriter[] properties, BeanPropertyWriter[] filteredProperties)
138     {
139         super(src._handledType);
140         _beanType = src._beanType;
141         _props = properties;
142         _filteredProps = filteredProperties;
143
144         _typeId = src._typeId;
145         _anyGetterWriter = src._anyGetterWriter;
146         _objectIdWriter = src._objectIdWriter;
147         _propertyFilterId = src._propertyFilterId;
148         _serializationShape = src._serializationShape;
149     }
150
151     protected BeanSerializerBase(BeanSerializerBase src,
152             ObjectIdWriter objectIdWriter)
153     {
154         this(src, objectIdWriter, src._propertyFilterId);
155     }
156     
157     /**
158      * @since 2.3
159      */

160     protected BeanSerializerBase(BeanSerializerBase src,
161             ObjectIdWriter objectIdWriter, Object filterId)
162     {
163         super(src._handledType);
164         _beanType = src._beanType;
165         _props = src._props;
166         _filteredProps = src._filteredProps;
167         
168         _typeId = src._typeId;
169         _anyGetterWriter = src._anyGetterWriter;
170         _objectIdWriter = objectIdWriter;
171         _propertyFilterId = filterId;
172         _serializationShape = src._serializationShape;
173     }
174
175     @Deprecated // since 2.8, remove soon
176     protected BeanSerializerBase(BeanSerializerBase src, String[] toIgnore)
177     {
178         this(src, ArrayBuilders.arrayToSet(toIgnore));
179     }
180     
181     protected BeanSerializerBase(BeanSerializerBase src, Set<String> toIgnore)
182     {
183         super(src._handledType);
184
185         _beanType = src._beanType;
186         final BeanPropertyWriter[] propsIn = src._props;
187         final BeanPropertyWriter[] fpropsIn = src._filteredProps;
188         final int len = propsIn.length;
189
190         ArrayList<BeanPropertyWriter> propsOut = new ArrayList<BeanPropertyWriter>(len);
191         ArrayList<BeanPropertyWriter> fpropsOut = (fpropsIn == null) ? null : new ArrayList<BeanPropertyWriter>(len);
192
193         for (int i = 0; i < len; ++i) {
194             BeanPropertyWriter bpw = propsIn[i];
195             // should be ignored?
196             if ((toIgnore != null) && toIgnore.contains(bpw.getName())) {
197                 continue;
198             }
199             propsOut.add(bpw);
200             if (fpropsIn != null) {
201                 fpropsOut.add(fpropsIn[i]);
202             }
203         }
204         _props = propsOut.toArray(new BeanPropertyWriter[propsOut.size()]);
205         _filteredProps = (fpropsOut == null) ? null : fpropsOut.toArray(new BeanPropertyWriter[fpropsOut.size()]);
206
207         _typeId = src._typeId;
208         _anyGetterWriter = src._anyGetterWriter;
209         _objectIdWriter = src._objectIdWriter;
210         _propertyFilterId = src._propertyFilterId;
211         _serializationShape = src._serializationShape;
212     }
213     
214     /**
215      * Mutant factory used for creating a new instance with different
216      * {@link ObjectIdWriter}.
217      * 
218      * @since 2.0
219      */

220     public abstract BeanSerializerBase withObjectIdWriter(ObjectIdWriter objectIdWriter);
221
222     /**
223      * Mutant factory used for creating a new instance with additional
224      * set of properties to ignore (from properties this instance otherwise has)
225      * 
226      * @since 2.8
227      */

228     protected abstract BeanSerializerBase withIgnorals(Set<String> toIgnore);
229     
230     /**
231      * Mutant factory used for creating a new instance with additional
232      * set of properties to ignore (from properties this instance otherwise has)
233      * 
234      * @deprecated since 2.8
235      */

236     @Deprecated
237     protected BeanSerializerBase withIgnorals(String[] toIgnore) {
238         return withIgnorals(ArrayBuilders.arrayToSet(toIgnore));
239     }
240
241     /**
242      * Mutant factory for creating a variant that output POJO as a
243      * JSON Array. Implementations may ignore this request if output
244      * as array is not possible (either at all, or reliably).
245      * 
246      * @since 2.1
247      */

248     protected abstract BeanSerializerBase asArraySerializer();
249
250     /**
251      * Mutant factory used for creating a new instance with different
252      * filter id (used with <code>JsonFilter</code> annotation)
253      * 
254      * @since 2.3
255      */

256     @Override
257     public abstract BeanSerializerBase withFilterId(Object filterId);
258
259     /**
260      * Mutant factory used for creating a new instance with modified set
261      * of properties.
262      *<p>
263      * Note: in 2.11.x, need to keep non-abstract for slightly better compatibility
264      * (XML module extends)
265      * 
266      * @since 2.11.1
267      */

268     protected BeanSerializerBase withProperties(BeanPropertyWriter[] properties,
269             BeanPropertyWriter[] filteredProperties) {
270         return this;
271     }
272
273     // Should be this, will be with 2.12:
274 //    protected abstract BeanSerializerBase withProperties(BeanPropertyWriter[] properties,
275 //            BeanPropertyWriter[] filteredProperties);
276
277     /**
278      * Copy-constructor that is useful for sub-classes that just want to
279      * copy all super-class properties without modifications.
280      */

281     protected BeanSerializerBase(BeanSerializerBase src) {
282         this(src, src._props, src._filteredProps);
283     }
284
285     /**
286      * Copy-constructor that will also rename properties with given prefix
287      * (if it's non-empty)
288      */

289     protected BeanSerializerBase(BeanSerializerBase src, NameTransformer unwrapper) {
290         this(src, rename(src._props, unwrapper), rename(src._filteredProps, unwrapper));
291     }
292     
293     private final static BeanPropertyWriter[] rename(BeanPropertyWriter[] props,
294             NameTransformer transformer)
295     {
296         if (props == null || props.length == 0 || transformer == null || transformer == NameTransformer.NOP) {
297             return props;
298         }
299         final int len = props.length;
300         BeanPropertyWriter[] result = new BeanPropertyWriter[len];
301         for (int i = 0; i < len; ++i) {
302             BeanPropertyWriter bpw = props[i];
303             if (bpw != null) {
304                 result[i] = bpw.rename(transformer);
305             }
306         }
307         return result;
308     }
309
310     /*
311     /**********************************************************
312     /* Post-construction processing: resolvable, contextual
313     /**********************************************************
314      */

315
316     /**
317      * We need to implement {@link ResolvableSerializer} to be able to
318      * properly handle cyclic type references.
319      */

320     @Override
321     public void resolve(SerializerProvider provider)
322         throws JsonMappingException
323     {
324         int filteredCount = (_filteredProps == null) ? 0 : _filteredProps.length;
325         for (int i = 0, len = _props.length; i < len; ++i) {
326             BeanPropertyWriter prop = _props[i];
327             // let's start with null serializer resolution actually
328             if (!prop.willSuppressNulls() && !prop.hasNullSerializer()) {
329                 JsonSerializer<Object> nullSer = provider.findNullValueSerializer(prop);
330                 if (nullSer != null) {
331                     prop.assignNullSerializer(nullSer);
332                     // also: remember to replace filtered property too? (see [JACKSON-364])
333                     if (i < filteredCount) {
334                         BeanPropertyWriter w2 = _filteredProps[i];
335                         if (w2 != null) {
336                             w2.assignNullSerializer(nullSer);
337                         }
338                     }
339                 }
340             }
341
342             if (prop.hasSerializer()) {
343                 continue;
344             }
345             // [databind#124]: allow use of converters
346             JsonSerializer<Object> ser = findConvertingSerializer(provider, prop);
347             if (ser == null) {
348                 // Was the serialization type hard-coded? If so, use it
349                 JavaType type = prop.getSerializationType();
350                 
351                 // It not, we can use declared return type if and only if declared type is final:
352                 // if not, we don't really know the actual type until we get the instance.
353                 if (type == null) {
354                     type = prop.getType();
355                     if (!type.isFinal()) {
356                         if (type.isContainerType() || type.containedTypeCount() > 0) {
357                             prop.setNonTrivialBaseType(type);
358                         }
359                         continue;
360                     }
361                 }
362                 ser = provider.findValueSerializer(type, prop);
363                 // 04-Feb-2010, tatu: We may have stashed type serializer for content types
364                 //   too, earlier; if so, it's time to connect the dots here:
365                 if (type.isContainerType()) {
366                     TypeSerializer typeSer = type.getContentType().getTypeHandler();
367                     if (typeSer != null) {
368                         // for now, can do this only for standard containers...
369                         if (ser instanceof ContainerSerializer<?>) {
370                             // ugly casts... but necessary
371                             @SuppressWarnings("unchecked")
372                             JsonSerializer<Object> ser2 = (JsonSerializer<Object>)((ContainerSerializer<?>) ser).withValueTypeSerializer(typeSer);
373                             ser = ser2;
374                         }
375                     }
376                 }
377             }
378             // and maybe replace filtered property too?
379             if (i < filteredCount) {
380                 BeanPropertyWriter w2 = _filteredProps[i];
381                 if (w2 != null) {
382                     w2.assignSerializer(ser);
383                     // 17-Mar-2017, tatu: Typically will lead to chained call to original property,
384                     //    which would lead to double set. Not a problem itself, except... unwrapping
385                     //    may require work to be done, which does lead to an actual issue.
386                     continue;
387                 }
388             }
389             prop.assignSerializer(ser);
390         }
391
392         // also, any-getter may need to be resolved
393         if (_anyGetterWriter != null) {
394             // 23-Feb-2015, tatu: Misleading, as this actually triggers call to contextualization...
395             _anyGetterWriter.resolve(provider);
396         }
397     }
398
399     /**
400      * Helper method that can be used to see if specified property is annotated
401      * to indicate use of a converter for property value (in case of container types,
402      * it is container type itself, not key or content type).
403      * 
404      * @since 2.2
405      */

406     protected JsonSerializer<Object> findConvertingSerializer(SerializerProvider provider,
407             BeanPropertyWriter prop)
408         throws JsonMappingException
409     {
410         final AnnotationIntrospector intr = provider.getAnnotationIntrospector();
411         if (intr != null) {
412             AnnotatedMember m = prop.getMember();
413             if (m != null) {
414                 Object convDef = intr.findSerializationConverter(m);
415                 if (convDef != null) {
416                     Converter<Object,Object> conv = provider.converterInstance(prop.getMember(), convDef);
417                     JavaType delegateType = conv.getOutputType(provider.getTypeFactory());
418                     // [databind#731]: Should skip if nominally java.lang.Object
419                     JsonSerializer<?> ser = delegateType.isJavaLangObject() ? null
420                             : provider.findValueSerializer(delegateType, prop);
421                     return new StdDelegatingSerializer(conv, delegateType, ser);
422                 }
423             }
424         }
425         return null;
426     }
427
428     @SuppressWarnings("incomplete-switch")
429     @Override
430     public JsonSerializer<?> createContextual(SerializerProvider provider,
431             BeanProperty property)
432         throws JsonMappingException
433     {
434         final AnnotationIntrospector intr = provider.getAnnotationIntrospector();
435         final AnnotatedMember accessor = (property == null || intr == null)
436                 ? null : property.getMember();
437         final SerializationConfig config = provider.getConfig();
438
439         // Let's start with one big transmutation: Enums that are annotated
440         // to serialize as Objects may want to revert
441         JsonFormat.Value format = findFormatOverrides(provider, property, _handledType);
442         JsonFormat.Shape shape = null;
443         if ((format != null) && format.hasShape()) {
444             shape = format.getShape();
445             // or, alternatively, asked to revert "back to" other representations...
446             if ((shape != JsonFormat.Shape.ANY) && (shape != _serializationShape)) {
447                 if (_beanType.isEnumType()) {
448                     switch (shape) {
449                     case STRING:
450                     case NUMBER:
451                     case NUMBER_INT:
452                         // 12-Oct-2014, tatu: May need to introspect full annotations... but
453                         //   for now, just do class ones
454                         BeanDescription desc = config.introspectClassAnnotations(_beanType);
455                         JsonSerializer<?> ser = EnumSerializer.construct(_beanType.getRawClass(),
456                                 provider.getConfig(), desc, format);
457                         return provider.handlePrimaryContextualization(ser, property);
458                     }
459                 // 16-Oct-2016, tatu: Ditto for `Map`, `Map.Entry` subtypes
460                 } else if (shape == JsonFormat.Shape.NATURAL) {
461                     if (_beanType.isMapLikeType() && Map.class.isAssignableFrom(_handledType)) {
462                         ;
463                     } else if (Map.Entry.class.isAssignableFrom(_handledType)) {
464                         JavaType mapEntryType = _beanType.findSuperType(Map.Entry.class);
465
466                         JavaType kt = mapEntryType.containedTypeOrUnknown(0);
467                         JavaType vt = mapEntryType.containedTypeOrUnknown(1);
468
469                         // 16-Oct-2016, tatu: could have problems with type handling, as we do not
470                         //   see if "static" typing is needed, nor look for `TypeSerializer` yet...
471                         JsonSerializer<?> ser = new MapEntrySerializer(_beanType, kt, vt,
472                                 falsenull, property);
473                         return provider.handlePrimaryContextualization(ser, property);
474                     }
475                 }
476             }
477         }
478
479         ObjectIdWriter oiw = _objectIdWriter;
480
481         // 16-Jun-2020, tatu: [databind#2759] means we need to handle reordering
482         //    at a later point
483         int idPropOrigIndex = 0;
484         Set<String> ignoredProps = null;
485         Object newFilterId = null;
486
487         // Then we may have an override for Object Id
488         if (accessor != null) {
489             JsonIgnoreProperties.Value ignorals = intr.findPropertyIgnorals(accessor);
490             if (ignorals != null) {
491                 ignoredProps = ignorals.findIgnoredForSerialization();
492             }
493             ObjectIdInfo objectIdInfo = intr.findObjectIdInfo(accessor);
494             if (objectIdInfo == null) {
495                 // no ObjectId override, but maybe ObjectIdRef?
496                 if (oiw != null) {
497                     objectIdInfo = intr.findObjectReferenceInfo(accessor, null);
498                     if (objectIdInfo != null) {
499                         oiw = _objectIdWriter.withAlwaysAsId(objectIdInfo.getAlwaysAsId());
500                     }
501                 }
502             } else {
503                 // Ugh: mostly copied from BeanDeserializerBase: but can't easily change it
504                 // to be able to move to SerializerProvider (where it really belongs)
505                 
506                 // 2.1: allow modifications by "id ref" annotations as well:
507                 objectIdInfo = intr.findObjectReferenceInfo(accessor, objectIdInfo);
508                 Class<?> implClass = objectIdInfo.getGeneratorType();
509                 JavaType type = provider.constructType(implClass);
510                 JavaType idType = provider.getTypeFactory().findTypeParameters(type, ObjectIdGenerator.class)[0];
511                 // Property-based generator is trickier
512                 if (implClass == ObjectIdGenerators.PropertyGenerator.class) { // most special one, needs extra work
513                     String propName = objectIdInfo.getPropertyName().getSimpleName();
514                     BeanPropertyWriter idProp = null;
515                     
516                     for (int i = 0, len = _props.length; ; ++i) {
517                         if (i == len) {
518                             provider.reportBadDefinition(_beanType, String.format(
519                                     "Invalid Object Id definition for %s: cannot find property with name '%s'",
520                                     handledType().getName(), propName));
521                         }
522                         BeanPropertyWriter prop = _props[i];
523                         if (propName.equals(prop.getName())) {
524                             idProp = prop;
525                             // Let's mark id prop to be moved as the first (may still get rearranged)
526                             // (although it may still get rearranged etc)
527                             idPropOrigIndex = i;
528                             break;
529                         }
530                     }
531                     idType = idProp.getType();
532                     ObjectIdGenerator<?> gen = new PropertyBasedObjectIdGenerator(objectIdInfo, idProp);
533                     oiw = ObjectIdWriter.construct(idType, (PropertyName) null, gen, objectIdInfo.getAlwaysAsId());
534                 } else { // other types need to be simpler
535                     ObjectIdGenerator<?> gen = provider.objectIdGeneratorInstance(accessor, objectIdInfo);
536                     oiw = ObjectIdWriter.construct(idType, objectIdInfo.getPropertyName(), gen,
537                             objectIdInfo.getAlwaysAsId());
538                 }
539             }
540             // Or change Filter Id in use?
541             Object filterId = intr.findFilterId(accessor);
542             if (filterId != null) {
543                 // but only consider case of adding a new filter id (no removal via annotation)
544                 if (_propertyFilterId == null || !filterId.equals(_propertyFilterId)) {
545                     newFilterId = filterId;
546                 }
547             }
548         }
549         // either way, need to resolve serializer:
550         BeanSerializerBase contextual = this;
551
552         // 16-Jun-2020, tatu: [databind#2759] must make copies, then reorder
553         if (idPropOrigIndex > 0) { // note: must shuffle both regular properties and filtered
554             final BeanPropertyWriter[] newProps = Arrays.copyOf(_props, _props.length);
555             BeanPropertyWriter bpw = newProps[idPropOrigIndex];
556             System.arraycopy(newProps, 0, newProps, 1, idPropOrigIndex);
557             newProps[0] = bpw;
558             final BeanPropertyWriter[] newFiltered;
559             if (_filteredProps == null) {
560                 newFiltered = null;
561             } else {
562                 newFiltered = Arrays.copyOf(_filteredProps, _filteredProps.length);
563                 bpw = newFiltered[idPropOrigIndex];
564                 System.arraycopy(newFiltered, 0, newFiltered, 1, idPropOrigIndex);
565                 newFiltered[0] = bpw;
566             }
567             contextual = contextual.withProperties(newProps, newFiltered);
568         }
569
570         if (oiw != null) {
571             JsonSerializer<?> ser = provider.findValueSerializer(oiw.idType, property);
572             oiw = oiw.withSerializer(ser);
573             if (oiw != _objectIdWriter) {
574                 contextual = contextual.withObjectIdWriter(oiw);
575             }
576         }
577         // And possibly add more properties to ignore
578         if ((ignoredProps != null) && !ignoredProps.isEmpty()) {
579             contextual = contextual.withIgnorals(ignoredProps);
580         }
581         if (newFilterId != null) {
582             contextual = contextual.withFilterId(newFilterId);
583         }
584
585         if (shape == null) {
586             shape = _serializationShape;
587         }
588         // last but not least; may need to transmute into as-array serialization
589         if (shape == JsonFormat.Shape.ARRAY) {
590             return contextual.asArraySerializer();
591         }
592         return contextual;
593     }
594
595     /*
596     /**********************************************************
597     /* Accessors
598     /**********************************************************
599      */

600
601     @Override
602     public Iterator<PropertyWriter> properties() {
603         return Arrays.<PropertyWriter>asList(_props).iterator();
604     }
605
606     /*
607     /**********************************************************
608     /* Partial JsonSerializer implementation
609     /**********************************************************
610      */

611
612     @Override
613     public boolean usesObjectId() {
614         return (_objectIdWriter != null);
615     }
616     
617     // Main serialization method left unimplemented
618     @Override
619     public abstract void serialize(Object bean, JsonGenerator gen, SerializerProvider provider)
620         throws IOException;
621
622     // Type-info-augmented case implemented as it does not usually differ between impls
623     @Override
624     public void serializeWithType(Object bean, JsonGenerator gen,
625             SerializerProvider provider, TypeSerializer typeSer)
626         throws IOException
627     {
628         if (_objectIdWriter != null) {
629             gen.setCurrentValue(bean); // [databind#631]
630             _serializeWithObjectId(bean, gen, provider, typeSer);
631             return;
632         }
633
634         gen.setCurrentValue(bean); // [databind#631]
635         WritableTypeId typeIdDef = _typeIdDef(typeSer, bean, JsonToken.START_OBJECT);
636         typeSer.writeTypePrefix(gen, typeIdDef);
637         if (_propertyFilterId != null) {
638             serializeFieldsFiltered(bean, gen, provider);
639         } else {
640             serializeFields(bean, gen, provider);
641         }
642         typeSer.writeTypeSuffix(gen, typeIdDef);
643     }
644
645     protected final void _serializeWithObjectId(Object bean, JsonGenerator gen, SerializerProvider provider,
646             boolean startEndObject) throws IOException
647     {
648         final ObjectIdWriter w = _objectIdWriter;
649         WritableObjectId objectId = provider.findObjectId(bean, w.generator);
650         // If possible, write as id already
651         if (objectId.writeAsId(gen, provider, w)) {
652             return;
653         }
654         // If not, need to inject the id:
655         Object id = objectId.generateId(bean);
656         if (w.alwaysAsId) {
657             w.serializer.serialize(id, gen, provider);
658             return;
659         }
660         if (startEndObject) {
661             gen.writeStartObject(bean);
662         }
663         objectId.writeAsField(gen, provider, w);
664         if (_propertyFilterId != null) {
665             serializeFieldsFiltered(bean, gen, provider);
666         } else {
667             serializeFields(bean, gen, provider);
668         }
669         if (startEndObject) {
670             gen.writeEndObject();
671         }
672     }
673     
674     protected final void _serializeWithObjectId(Object bean, JsonGenerator gen, SerializerProvider provider,
675             TypeSerializer typeSer) throws IOException
676     {
677         final ObjectIdWriter w = _objectIdWriter;
678         WritableObjectId objectId = provider.findObjectId(bean, w.generator);
679         // If possible, write as id already
680         if (objectId.writeAsId(gen, provider, w)) {
681             return;
682         }
683         // If not, need to inject the id:
684         Object id = objectId.generateId(bean);
685         if (w.alwaysAsId) {
686             w.serializer.serialize(id, gen, provider);
687             return;
688         }
689         _serializeObjectId(bean, gen, provider, typeSer, objectId);
690     }
691
692     protected  void _serializeObjectId(Object bean, JsonGenerator g,
693             SerializerProvider provider,
694             TypeSerializer typeSer, WritableObjectId objectId) throws IOException
695     {
696         final ObjectIdWriter w = _objectIdWriter;
697         WritableTypeId typeIdDef = _typeIdDef(typeSer, bean, JsonToken.START_OBJECT);
698
699         typeSer.writeTypePrefix(g, typeIdDef);
700         objectId.writeAsField(g, provider, w);
701         if (_propertyFilterId != null) {
702             serializeFieldsFiltered(bean, g, provider);
703         } else {
704             serializeFields(bean, g, provider);
705         }
706         typeSer.writeTypeSuffix(g, typeIdDef);
707     }
708
709     /**
710      * @since 2.9
711      */

712     protected final WritableTypeId _typeIdDef(TypeSerializer typeSer,
713             Object bean, JsonToken valueShape) {
714         if (_typeId == null) {
715             return typeSer.typeId(bean, valueShape);
716         }
717         Object typeId = _typeId.getValue(bean);
718         if (typeId == null) {
719             // 28-Jun-2017, tatu: Is this really needed? Unchanged from 2.8 but...
720             typeId = "";
721         }
722         return typeSer.typeId(bean, valueShape, typeId);
723     }
724
725     @Deprecated // since 2.9
726     protected final String _customTypeId(Object bean)
727     {
728         final Object typeId = _typeId.getValue(bean);
729         if (typeId == null) {
730             return "";
731         }
732         return (typeId instanceof String) ? (String) typeId : typeId.toString();
733     }
734
735     /*
736     /**********************************************************
737     /* Field serialization methods
738     /**********************************************************
739      */

740
741     protected void serializeFields(Object bean, JsonGenerator gen, SerializerProvider provider)
742         throws IOException
743     {
744         final BeanPropertyWriter[] props;
745         if (_filteredProps != null && provider.getActiveView() != null) {
746             props = _filteredProps;
747         } else {
748             props = _props;
749         }
750         int i = 0;
751         try {
752             for (final int len = props.length; i < len; ++i) {
753                 BeanPropertyWriter prop = props[i];
754                 if (prop != null) { // can have nulls in filtered list
755                     prop.serializeAsField(bean, gen, provider);
756                 }
757             }
758             if (_anyGetterWriter != null) {
759                 _anyGetterWriter.getAndSerialize(bean, gen, provider);
760             }
761         } catch (Exception e) {
762             String name = (i == props.length) ? "[anySetter]" : props[i].getName();
763             wrapAndThrow(provider, e, bean, name);
764         } catch (StackOverflowError e) {
765             // 04-Sep-2009, tatu: Dealing with this is tricky, since we don't have many
766             //   stack frames to spare... just one or two; can't make many calls.
767
768             // 10-Dec-2015, tatu: and due to above, avoid "from" method, call ctor directly:
769             //JsonMappingException mapE = JsonMappingException.from(gen, "Infinite recursion (StackOverflowError)", e);
770             JsonMappingException mapE = new JsonMappingException(gen, "Infinite recursion (StackOverflowError)", e);
771
772              String name = (i == props.length) ? "[anySetter]" : props[i].getName();
773             mapE.prependPath(new JsonMappingException.Reference(bean, name));
774             throw mapE;
775         }
776     }
777
778     /**
779      * Alternative serialization method that gets called when there is a
780      * {@link PropertyFilter} that needs to be called to determine
781      * which properties are to be serialized (and possibly how)
782      */

783     protected void serializeFieldsFiltered(Object bean, JsonGenerator gen,
784             SerializerProvider provider)
785         throws IOException, JsonGenerationException
786     {
787         /* note: almost verbatim copy of "serializeFields"; copied (instead of merged)
788          * so that old method need not add check for existence of filter.
789          */

790         final BeanPropertyWriter[] props;
791         if (_filteredProps != null && provider.getActiveView() != null) {
792             props = _filteredProps;
793         } else {
794             props = _props;
795         }
796         final PropertyFilter filter = findPropertyFilter(provider, _propertyFilterId, bean);
797         // better also allow missing filter actually..
798         if (filter == null) {
799             serializeFields(bean, gen, provider);
800             return;
801         }
802         int i = 0;
803         try {
804             for (final int len = props.length; i < len; ++i) {
805                 BeanPropertyWriter prop = props[i];
806                 if (prop != null) { // can have nulls in filtered list
807                     filter.serializeAsField(bean, gen, provider, prop);
808                 }
809             }
810             if (_anyGetterWriter != null) {
811                 _anyGetterWriter.getAndFilter(bean, gen, provider, filter);
812             }
813         } catch (Exception e) {
814             String name = (i == props.length) ? "[anySetter]" : props[i].getName();
815             wrapAndThrow(provider, e, bean, name);
816         } catch (StackOverflowError e) {
817             // Minimize call depth since we are close to fail:
818             //JsonMappingException mapE = JsonMappingException.from(gen, "Infinite recursion (StackOverflowError)", e);
819             JsonMappingException mapE = new JsonMappingException(gen, "Infinite recursion (StackOverflowError)", e);
820             String name = (i == props.length) ? "[anySetter]" : props[i].getName();
821             mapE.prependPath(new JsonMappingException.Reference(bean, name));
822             throw mapE;
823         }
824     }
825
826     @Deprecated
827     @Override
828     public JsonNode getSchema(SerializerProvider provider, Type typeHint)
829         throws JsonMappingException
830     {
831         ObjectNode o = createSchemaNode("object"true);
832         // [JACKSON-813]: Add optional JSON Schema id attribute, if found
833         // NOTE: not optimal, does NOT go through AnnotationIntrospector etc:
834         JsonSerializableSchema ann = _handledType.getAnnotation(JsonSerializableSchema.class);
835         if (ann != null) {
836             String id = ann.id();
837             if (id != null && id.length() > 0) {
838                 o.put("id", id);
839             }
840         }
841  
842         //todo: should the classname go in the title?
843         //o.put("title", _className);
844         ObjectNode propertiesNode = o.objectNode();
845         final PropertyFilter filter;
846         if (_propertyFilterId != null) {
847             filter = findPropertyFilter(provider, _propertyFilterId, null);
848         } else {
849             filter = null;
850         }
851                 
852         for (int i = 0; i < _props.length; i++) {
853             BeanPropertyWriter prop = _props[i];
854             if (filter == null) {
855                 prop.depositSchemaProperty(propertiesNode, provider);
856             } else {
857                 filter.depositSchemaProperty(prop, propertiesNode, provider);
858             }
859
860         }
861         o.set("properties", propertiesNode);
862         return o;
863     }
864     
865     @Override
866     public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
867         throws JsonMappingException
868     {
869         //deposit your output format 
870         if (visitor == null) {
871             return;
872         }
873         JsonObjectFormatVisitor objectVisitor = visitor.expectObjectFormat(typeHint);
874         if (objectVisitor == null) {
875             return;
876         }
877         final SerializerProvider provider = visitor.getProvider();
878         if (_propertyFilterId != null) {
879             PropertyFilter filter = findPropertyFilter(visitor.getProvider(),
880                     _propertyFilterId, null);
881             for (int i = 0, end = _props.length; i < end; ++i) {
882                 filter.depositSchemaProperty(_props[i], objectVisitor, provider);
883             }
884         } else {
885             Class<?> view = ((_filteredProps == null) || (provider == null))
886                     ? null : provider.getActiveView();
887             final BeanPropertyWriter[] props;
888             if (view != null) {
889                 props = _filteredProps;
890             } else {
891                 props = _props;
892             }
893
894             for (int i = 0, end = props.length; i < end; ++i) {
895                 BeanPropertyWriter prop = props[i];
896                 if (prop != null) { // may be filtered out unconditionally
897                     prop.depositSchemaProperty(objectVisitor, provider);
898                 }
899             }
900         }
901     }
902 }
903