• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.IgnorePropertiesUtil;
30 import com.fasterxml.jackson.databind.util.NameTransformer;
31 
32 /**
33  * Base class both for the standard bean serializer, and couple
34  * of variants that only differ in small details.
35  * Can be used for custom bean serializers as well, although that
36  * is not the primary design goal.
37  */
38 @SuppressWarnings("serial")
39 public abstract class BeanSerializerBase
40     extends StdSerializer<Object>
41     implements ContextualSerializer, ResolvableSerializer,
42         JsonFormatVisitable, SchemaAware
43 {
44     protected final static PropertyName NAME_FOR_OBJECT_REF = new PropertyName("#object-ref");
45 
46     final protected static BeanPropertyWriter[] NO_PROPS = new BeanPropertyWriter[0];
47 
48     /*
49     /**********************************************************
50     /* Configuration
51     /**********************************************************
52      */
53 
54     /**
55      * @since 2.9
56      */
57     final protected JavaType _beanType;
58 
59     /**
60      * Writers used for outputting actual property values
61      */
62     final protected BeanPropertyWriter[] _props;
63 
64     /**
65      * Optional filters used to suppress output of properties that
66      * are only to be included in certain views
67      */
68     final protected BeanPropertyWriter[] _filteredProps;
69 
70     /**
71      * Handler for {@link com.fasterxml.jackson.annotation.JsonAnyGetter}
72      * annotated properties
73      */
74     final protected AnyGetterWriter _anyGetterWriter;
75 
76     /**
77      * Id of the bean property filter to use, if any; null if none.
78      */
79     final protected Object _propertyFilterId;
80 
81     /**
82      * If using custom type ids (usually via getter, or field), this is the
83      * reference to that member.
84      */
85     final protected AnnotatedMember _typeId;
86 
87     /**
88      * If this POJO can be alternatively serialized using just an object id
89      * to denote a reference to previously serialized object,
90      * this Object will handle details.
91      */
92     final protected ObjectIdWriter _objectIdWriter;
93 
94     /**
95      * Requested shape from bean class annotations.
96      */
97     final protected JsonFormat.Shape _serializationShape;
98 
99     /*
100     /**********************************************************
101     /* Life-cycle: constructors
102     /**********************************************************
103      */
104 
105     /**
106      * Constructor used by {@link BeanSerializerBuilder} to create an
107      * instance
108      *
109      * @param type Nominal type of values handled by this serializer
110      * @param builder Builder for accessing other collected information
111      */
BeanSerializerBase(JavaType type, BeanSerializerBuilder builder, BeanPropertyWriter[] properties, BeanPropertyWriter[] filteredProperties)112     protected BeanSerializerBase(JavaType type, BeanSerializerBuilder builder,
113             BeanPropertyWriter[] properties, BeanPropertyWriter[] filteredProperties)
114     {
115         super(type);
116         _beanType = type;
117         _props = properties;
118         _filteredProps = filteredProperties;
119         if (builder == null) { // mostly for testing
120             // 20-Sep-2019, tatu: Actually not just that but also "dummy" serializer for
121             //     case of no bean properties, too
122             _typeId = null;
123             _anyGetterWriter = null;
124             _propertyFilterId = null;
125             _objectIdWriter = null;
126             _serializationShape = null;
127         } else {
128             _typeId = builder.getTypeId();
129             _anyGetterWriter = builder.getAnyGetter();
130             _propertyFilterId = builder.getFilterId();
131             _objectIdWriter = builder.getObjectIdWriter();
132             final JsonFormat.Value format = builder.getBeanDescription().findExpectedFormat(null);
133             _serializationShape = format.getShape();
134         }
135     }
136 
BeanSerializerBase(BeanSerializerBase src, BeanPropertyWriter[] properties, BeanPropertyWriter[] filteredProperties)137     protected BeanSerializerBase(BeanSerializerBase src,
138             BeanPropertyWriter[] properties, BeanPropertyWriter[] filteredProperties)
139     {
140         super(src._handledType);
141         _beanType = src._beanType;
142         _props = properties;
143         _filteredProps = filteredProperties;
144 
145         _typeId = src._typeId;
146         _anyGetterWriter = src._anyGetterWriter;
147         _objectIdWriter = src._objectIdWriter;
148         _propertyFilterId = src._propertyFilterId;
149         _serializationShape = src._serializationShape;
150     }
151 
BeanSerializerBase(BeanSerializerBase src, ObjectIdWriter objectIdWriter)152     protected BeanSerializerBase(BeanSerializerBase src,
153             ObjectIdWriter objectIdWriter)
154     {
155         this(src, objectIdWriter, src._propertyFilterId);
156     }
157 
158     /**
159      * @since 2.3
160      */
BeanSerializerBase(BeanSerializerBase src, ObjectIdWriter objectIdWriter, Object filterId)161     protected BeanSerializerBase(BeanSerializerBase src,
162             ObjectIdWriter objectIdWriter, Object filterId)
163     {
164         super(src._handledType);
165         _beanType = src._beanType;
166         _props = src._props;
167         _filteredProps = src._filteredProps;
168 
169         _typeId = src._typeId;
170         _anyGetterWriter = src._anyGetterWriter;
171         _objectIdWriter = objectIdWriter;
172         _propertyFilterId = filterId;
173         _serializationShape = src._serializationShape;
174     }
175 
176     @Deprecated // since 2.8, remove soon
BeanSerializerBase(BeanSerializerBase src, String[] toIgnore)177     protected BeanSerializerBase(BeanSerializerBase src, String[] toIgnore)
178     {
179         this(src, ArrayBuilders.arrayToSet(toIgnore), null);
180     }
181 
182     @Deprecated // since 2.12
BeanSerializerBase(BeanSerializerBase src, Set<String> toIgnore)183     protected BeanSerializerBase(BeanSerializerBase src, Set<String> toIgnore) {
184         this(src, toIgnore, null);
185     }
186 
BeanSerializerBase(BeanSerializerBase src, Set<String> toIgnore, Set<String> toInclude)187     protected BeanSerializerBase(BeanSerializerBase src, Set<String> toIgnore, Set<String> toInclude)
188     {
189         super(src._handledType);
190 
191         _beanType = src._beanType;
192         final BeanPropertyWriter[] propsIn = src._props;
193         final BeanPropertyWriter[] fpropsIn = src._filteredProps;
194         final int len = propsIn.length;
195 
196         ArrayList<BeanPropertyWriter> propsOut = new ArrayList<BeanPropertyWriter>(len);
197         ArrayList<BeanPropertyWriter> fpropsOut = (fpropsIn == null) ? null : new ArrayList<BeanPropertyWriter>(len);
198 
199         for (int i = 0; i < len; ++i) {
200             BeanPropertyWriter bpw = propsIn[i];
201             // should be ignored?
202             if (IgnorePropertiesUtil.shouldIgnore(bpw.getName(), toIgnore, toInclude)) {
203                 continue;
204             }
205             propsOut.add(bpw);
206             if (fpropsIn != null) {
207                 fpropsOut.add(fpropsIn[i]);
208             }
209         }
210         _props = propsOut.toArray(new BeanPropertyWriter[propsOut.size()]);
211         _filteredProps = (fpropsOut == null) ? null : fpropsOut.toArray(new BeanPropertyWriter[fpropsOut.size()]);
212 
213         _typeId = src._typeId;
214         _anyGetterWriter = src._anyGetterWriter;
215         _objectIdWriter = src._objectIdWriter;
216         _propertyFilterId = src._propertyFilterId;
217         _serializationShape = src._serializationShape;
218     }
219 
220     /**
221      * Mutant factory used for creating a new instance with different
222      * {@link ObjectIdWriter}.
223      *
224      * @since 2.0
225      */
withObjectIdWriter(ObjectIdWriter objectIdWriter)226     public abstract BeanSerializerBase withObjectIdWriter(ObjectIdWriter objectIdWriter);
227 
228     /**
229      * Mutant factory used for creating a new instance with additional
230      * set of properties to ignore (from properties this instance otherwise has)
231      *
232      * @since 2.8
233      * @deprecated Since 2.12
234      */
235     @Deprecated // since 2.12
withIgnorals(Set<String> toIgnore)236     protected BeanSerializerBase withIgnorals(Set<String> toIgnore) {
237         return withByNameInclusion(toIgnore, null);
238     }
239 
240     /**
241      * Mutant factory used for creating a new instance with additional
242      * set of properties to ignore or include (from properties this instance otherwise has)
243      *
244      * @since 2.12
245      */
withByNameInclusion(Set<String> toIgnore, Set<String> toInclude)246     protected abstract BeanSerializerBase withByNameInclusion(Set<String> toIgnore,
247             Set<String> toInclude);
248 
249     /**
250      * Mutant factory used for creating a new instance with additional
251      * set of properties to ignore (from properties this instance otherwise has)
252      *
253      * @deprecated since 2.8
254      */
255     @Deprecated
withIgnorals(String[] toIgnore)256     protected BeanSerializerBase withIgnorals(String[] toIgnore) {
257         return withIgnorals(ArrayBuilders.arrayToSet(toIgnore));
258     }
259 
260     /**
261      * Mutant factory for creating a variant that output POJO as a
262      * JSON Array. Implementations may ignore this request if output
263      * as array is not possible (either at all, or reliably).
264      *
265      * @since 2.1
266      */
asArraySerializer()267     protected abstract BeanSerializerBase asArraySerializer();
268 
269     /**
270      * Mutant factory used for creating a new instance with different
271      * filter id (used with <code>JsonFilter</code> annotation)
272      *
273      * @since 2.3
274      */
275     @Override
withFilterId(Object filterId)276     public abstract BeanSerializerBase withFilterId(Object filterId);
277 
278     /**
279      * Mutant factory used for creating a new instance with modified set
280      * of properties.
281      *<p>
282      * Note: in 2.11.x, need to keep non-abstract for slightly better compatibility
283      * (XML module extends)
284      *
285      * @since 2.11.1
286      */
withProperties(BeanPropertyWriter[] properties, BeanPropertyWriter[] filteredProperties)287     protected abstract BeanSerializerBase withProperties(BeanPropertyWriter[] properties,
288             BeanPropertyWriter[] filteredProperties);
289 
290     /**
291      * Copy-constructor that is useful for sub-classes that just want to
292      * copy all super-class properties without modifications.
293      */
BeanSerializerBase(BeanSerializerBase src)294     protected BeanSerializerBase(BeanSerializerBase src) {
295         this(src, src._props, src._filteredProps);
296     }
297 
298     /**
299      * Copy-constructor that will also rename properties with given prefix
300      * (if it's non-empty)
301      */
BeanSerializerBase(BeanSerializerBase src, NameTransformer unwrapper)302     protected BeanSerializerBase(BeanSerializerBase src, NameTransformer unwrapper) {
303         this(src, rename(src._props, unwrapper), rename(src._filteredProps, unwrapper));
304     }
305 
rename(BeanPropertyWriter[] props, NameTransformer transformer)306     private final static BeanPropertyWriter[] rename(BeanPropertyWriter[] props,
307             NameTransformer transformer)
308     {
309         if (props == null || props.length == 0 || transformer == null || transformer == NameTransformer.NOP) {
310             return props;
311         }
312         final int len = props.length;
313         BeanPropertyWriter[] result = new BeanPropertyWriter[len];
314         for (int i = 0; i < len; ++i) {
315             BeanPropertyWriter bpw = props[i];
316             if (bpw != null) {
317                 result[i] = bpw.rename(transformer);
318             }
319         }
320         return result;
321     }
322 
323     /*
324     /**********************************************************
325     /* Post-construction processing: resolvable, contextual
326     /**********************************************************
327      */
328 
329     /**
330      * We need to implement {@link ResolvableSerializer} to be able to
331      * properly handle cyclic type references.
332      */
333     @Override
resolve(SerializerProvider provider)334     public void resolve(SerializerProvider provider)
335         throws JsonMappingException
336     {
337         int filteredCount = (_filteredProps == null) ? 0 : _filteredProps.length;
338         for (int i = 0, len = _props.length; i < len; ++i) {
339             BeanPropertyWriter prop = _props[i];
340             // let's start with null serializer resolution actually
341             if (!prop.willSuppressNulls() && !prop.hasNullSerializer()) {
342                 JsonSerializer<Object> nullSer = provider.findNullValueSerializer(prop);
343                 if (nullSer != null) {
344                     prop.assignNullSerializer(nullSer);
345                     // also: remember to replace filtered property too? (see [JACKSON-364])
346                     if (i < filteredCount) {
347                         BeanPropertyWriter w2 = _filteredProps[i];
348                         if (w2 != null) {
349                             w2.assignNullSerializer(nullSer);
350                         }
351                     }
352                 }
353             }
354 
355             if (prop.hasSerializer()) {
356                 continue;
357             }
358             // [databind#124]: allow use of converters
359             JsonSerializer<Object> ser = findConvertingSerializer(provider, prop);
360             if (ser == null) {
361                 // Was the serialization type hard-coded? If so, use it
362                 JavaType type = prop.getSerializationType();
363 
364                 // It not, we can use declared return type if and only if declared type is final:
365                 // if not, we don't really know the actual type until we get the instance.
366                 if (type == null) {
367                     type = prop.getType();
368                     if (!type.isFinal()) {
369                         if (type.isContainerType() || type.containedTypeCount() > 0) {
370                             prop.setNonTrivialBaseType(type);
371                         }
372                         continue;
373                     }
374                 }
375                 ser = provider.findValueSerializer(type, prop);
376                 // 04-Feb-2010, tatu: We may have stashed type serializer for content types
377                 //   too, earlier; if so, it's time to connect the dots here:
378                 if (type.isContainerType()) {
379                     TypeSerializer typeSer = type.getContentType().getTypeHandler();
380                     if (typeSer != null) {
381                         // for now, can do this only for standard containers...
382                         if (ser instanceof ContainerSerializer<?>) {
383                             // ugly casts... but necessary
384                             @SuppressWarnings("unchecked")
385                             JsonSerializer<Object> ser2 = (JsonSerializer<Object>)((ContainerSerializer<?>) ser).withValueTypeSerializer(typeSer);
386                             ser = ser2;
387                         }
388                     }
389                 }
390             }
391             // and maybe replace filtered property too?
392             if (i < filteredCount) {
393                 BeanPropertyWriter w2 = _filteredProps[i];
394                 if (w2 != null) {
395                     w2.assignSerializer(ser);
396                     // 17-Mar-2017, tatu: Typically will lead to chained call to original property,
397                     //    which would lead to double set. Not a problem itself, except... unwrapping
398                     //    may require work to be done, which does lead to an actual issue.
399                     continue;
400                 }
401             }
402             prop.assignSerializer(ser);
403         }
404 
405         // also, any-getter may need to be resolved
406         if (_anyGetterWriter != null) {
407             // 23-Feb-2015, tatu: Misleading, as this actually triggers call to contextualization...
408             _anyGetterWriter.resolve(provider);
409         }
410     }
411 
412     /**
413      * Helper method that can be used to see if specified property is annotated
414      * to indicate use of a converter for property value (in case of container types,
415      * it is container type itself, not key or content type).
416      *
417      * @since 2.2
418      */
findConvertingSerializer(SerializerProvider provider, BeanPropertyWriter prop)419     protected JsonSerializer<Object> findConvertingSerializer(SerializerProvider provider,
420             BeanPropertyWriter prop)
421         throws JsonMappingException
422     {
423         final AnnotationIntrospector intr = provider.getAnnotationIntrospector();
424         if (intr != null) {
425             AnnotatedMember m = prop.getMember();
426             if (m != null) {
427                 Object convDef = intr.findSerializationConverter(m);
428                 if (convDef != null) {
429                     Converter<Object,Object> conv = provider.converterInstance(prop.getMember(), convDef);
430                     JavaType delegateType = conv.getOutputType(provider.getTypeFactory());
431                     // [databind#731]: Should skip if nominally java.lang.Object
432                     JsonSerializer<?> ser = delegateType.isJavaLangObject() ? null
433                             : provider.findValueSerializer(delegateType, prop);
434                     return new StdDelegatingSerializer(conv, delegateType, ser);
435                 }
436             }
437         }
438         return null;
439     }
440 
441     @SuppressWarnings("incomplete-switch")
442     @Override
createContextual(SerializerProvider provider, BeanProperty property)443     public JsonSerializer<?> createContextual(SerializerProvider provider,
444             BeanProperty property)
445         throws JsonMappingException
446     {
447         final AnnotationIntrospector intr = provider.getAnnotationIntrospector();
448         final AnnotatedMember accessor = (property == null || intr == null)
449                 ? null : property.getMember();
450         final SerializationConfig config = provider.getConfig();
451 
452         // Let's start with one big transmutation: Enums that are annotated
453         // to serialize as Objects may want to revert
454         JsonFormat.Value format = findFormatOverrides(provider, property, _handledType);
455         JsonFormat.Shape shape = null;
456         if ((format != null) && format.hasShape()) {
457             shape = format.getShape();
458             // or, alternatively, asked to revert "back to" other representations...
459             if ((shape != JsonFormat.Shape.ANY) && (shape != _serializationShape)) {
460                 if (_beanType.isEnumType()) {
461                     switch (shape) {
462                     case STRING:
463                     case NUMBER:
464                     case NUMBER_INT:
465                         // 12-Oct-2014, tatu: May need to introspect full annotations... but
466                         //   for now, just do class ones
467                         BeanDescription desc = config.introspectClassAnnotations(_beanType);
468                         JsonSerializer<?> ser = EnumSerializer.construct(_beanType.getRawClass(),
469                                 provider.getConfig(), desc, format);
470                         return provider.handlePrimaryContextualization(ser, property);
471                     }
472                 // 16-Oct-2016, tatu: Ditto for `Map`, `Map.Entry` subtypes
473                 } else if (shape == JsonFormat.Shape.NATURAL) {
474                     if (_beanType.isMapLikeType() && Map.class.isAssignableFrom(_handledType)) {
475                         ;
476                     } else if (Map.Entry.class.isAssignableFrom(_handledType)) {
477                         JavaType mapEntryType = _beanType.findSuperType(Map.Entry.class);
478 
479                         JavaType kt = mapEntryType.containedTypeOrUnknown(0);
480                         JavaType vt = mapEntryType.containedTypeOrUnknown(1);
481 
482                         // 16-Oct-2016, tatu: could have problems with type handling, as we do not
483                         //   see if "static" typing is needed, nor look for `TypeSerializer` yet...
484                         JsonSerializer<?> ser = new MapEntrySerializer(_beanType, kt, vt,
485                                 false, null, property);
486                         return provider.handlePrimaryContextualization(ser, property);
487                     }
488                 }
489             }
490         }
491 
492         ObjectIdWriter oiw = _objectIdWriter;
493 
494         // 16-Jun-2020, tatu: [databind#2759] means we need to handle reordering
495         //    at a later point
496         int idPropOrigIndex = 0;
497         Set<String> ignoredProps = null;
498         Set<String> includedProps = null;
499         Object newFilterId = null;
500 
501         // Then we may have an override for Object Id
502         if (accessor != null) {
503             ignoredProps = intr.findPropertyIgnoralByName(config, accessor).findIgnoredForSerialization();
504             includedProps = intr.findPropertyInclusionByName(config, accessor).getIncluded();
505             ObjectIdInfo objectIdInfo = intr.findObjectIdInfo(accessor);
506             if (objectIdInfo == null) {
507                 // no ObjectId override, but maybe ObjectIdRef?
508                 if (oiw != null) {
509                     objectIdInfo = intr.findObjectReferenceInfo(accessor, null);
510                     if (objectIdInfo != null) {
511                         oiw = _objectIdWriter.withAlwaysAsId(objectIdInfo.getAlwaysAsId());
512                     }
513                 }
514             } else {
515                 // Ugh: mostly copied from BeanDeserializerBase: but can't easily change it
516                 // to be able to move to SerializerProvider (where it really belongs)
517 
518                 // 2.1: allow modifications by "id ref" annotations as well:
519                 objectIdInfo = intr.findObjectReferenceInfo(accessor, objectIdInfo);
520                 Class<?> implClass = objectIdInfo.getGeneratorType();
521                 JavaType type = provider.constructType(implClass);
522                 JavaType idType = provider.getTypeFactory().findTypeParameters(type, ObjectIdGenerator.class)[0];
523                 // Property-based generator is trickier
524                 if (implClass == ObjectIdGenerators.PropertyGenerator.class) { // most special one, needs extra work
525                     String propName = objectIdInfo.getPropertyName().getSimpleName();
526                     BeanPropertyWriter idProp = null;
527 
528                     for (int i = 0, len = _props.length; ; ++i) {
529                         if (i == len) {
530                             provider.reportBadDefinition(_beanType, String.format(
531                                     "Invalid Object Id definition for %s: cannot find property with name '%s'",
532                                     handledType().getName(), propName));
533                         }
534                         BeanPropertyWriter prop = _props[i];
535                         if (propName.equals(prop.getName())) {
536                             idProp = prop;
537                             // Let's mark id prop to be moved as the first (may still get rearranged)
538                             // (although it may still get rearranged etc)
539                             idPropOrigIndex = i;
540                             break;
541                         }
542                     }
543                     idType = idProp.getType();
544                     ObjectIdGenerator<?> gen = new PropertyBasedObjectIdGenerator(objectIdInfo, idProp);
545                     oiw = ObjectIdWriter.construct(idType, (PropertyName) null, gen, objectIdInfo.getAlwaysAsId());
546                 } else { // other types need to be simpler
547                     ObjectIdGenerator<?> gen = provider.objectIdGeneratorInstance(accessor, objectIdInfo);
548                     oiw = ObjectIdWriter.construct(idType, objectIdInfo.getPropertyName(), gen,
549                             objectIdInfo.getAlwaysAsId());
550                 }
551             }
552             // Or change Filter Id in use?
553             Object filterId = intr.findFilterId(accessor);
554             if (filterId != null) {
555                 // but only consider case of adding a new filter id (no removal via annotation)
556                 if (_propertyFilterId == null || !filterId.equals(_propertyFilterId)) {
557                     newFilterId = filterId;
558                 }
559             }
560         }
561         // either way, need to resolve serializer:
562         BeanSerializerBase contextual = this;
563 
564         // 16-Jun-2020, tatu: [databind#2759] must make copies, then reorder
565         if (idPropOrigIndex > 0) { // note: must shuffle both regular properties and filtered
566             final BeanPropertyWriter[] newProps = Arrays.copyOf(_props, _props.length);
567             BeanPropertyWriter bpw = newProps[idPropOrigIndex];
568             System.arraycopy(newProps, 0, newProps, 1, idPropOrigIndex);
569             newProps[0] = bpw;
570             final BeanPropertyWriter[] newFiltered;
571             if (_filteredProps == null) {
572                 newFiltered = null;
573             } else {
574                 newFiltered = Arrays.copyOf(_filteredProps, _filteredProps.length);
575                 bpw = newFiltered[idPropOrigIndex];
576                 System.arraycopy(newFiltered, 0, newFiltered, 1, idPropOrigIndex);
577                 newFiltered[0] = bpw;
578             }
579             contextual = contextual.withProperties(newProps, newFiltered);
580         }
581 
582         if (oiw != null) {
583             JsonSerializer<?> ser = provider.findValueSerializer(oiw.idType, property);
584             oiw = oiw.withSerializer(ser);
585             if (oiw != _objectIdWriter) {
586                 contextual = contextual.withObjectIdWriter(oiw);
587             }
588         }
589         // Possibly change inclusions: for ignored, only non-empty set matters;
590         // for inclusion `null` means "not defined" but empty "include nothing":
591         if (((ignoredProps != null) && !ignoredProps.isEmpty())
592                 || (includedProps != null)) {
593             contextual = contextual.withByNameInclusion(ignoredProps, includedProps);
594         }
595         if (newFilterId != null) {
596             contextual = contextual.withFilterId(newFilterId);
597         }
598 
599         if (shape == null) {
600             shape = _serializationShape;
601         }
602         // last but not least; may need to transmute into as-array serialization
603         if (shape == JsonFormat.Shape.ARRAY) {
604             return contextual.asArraySerializer();
605         }
606         return contextual;
607     }
608 
609     /*
610     /**********************************************************
611     /* Accessors
612     /**********************************************************
613      */
614 
615     @Override
properties()616     public Iterator<PropertyWriter> properties() {
617         return Arrays.<PropertyWriter>asList(_props).iterator();
618     }
619 
620     /*
621     /**********************************************************
622     /* Partial JsonSerializer implementation
623     /**********************************************************
624      */
625 
626     @Override
usesObjectId()627     public boolean usesObjectId() {
628         return (_objectIdWriter != null);
629     }
630 
631     // Main serialization method left unimplemented
632     @Override
serialize(Object bean, JsonGenerator gen, SerializerProvider provider)633     public abstract void serialize(Object bean, JsonGenerator gen, SerializerProvider provider)
634         throws IOException;
635 
636     // Type-info-augmented case implemented as it does not usually differ between impls
637     @Override
serializeWithType(Object bean, JsonGenerator gen, SerializerProvider provider, TypeSerializer typeSer)638     public void serializeWithType(Object bean, JsonGenerator gen,
639             SerializerProvider provider, TypeSerializer typeSer)
640         throws IOException
641     {
642         if (_objectIdWriter != null) {
643             gen.setCurrentValue(bean); // [databind#631]
644             _serializeWithObjectId(bean, gen, provider, typeSer);
645             return;
646         }
647 
648         gen.setCurrentValue(bean); // [databind#631]
649         WritableTypeId typeIdDef = _typeIdDef(typeSer, bean, JsonToken.START_OBJECT);
650         typeSer.writeTypePrefix(gen, typeIdDef);
651         if (_propertyFilterId != null) {
652             serializeFieldsFiltered(bean, gen, provider);
653         } else {
654             serializeFields(bean, gen, provider);
655         }
656         typeSer.writeTypeSuffix(gen, typeIdDef);
657     }
658 
_serializeWithObjectId(Object bean, JsonGenerator gen, SerializerProvider provider, boolean startEndObject)659     protected final void _serializeWithObjectId(Object bean, JsonGenerator gen, SerializerProvider provider,
660             boolean startEndObject) throws IOException
661     {
662         final ObjectIdWriter w = _objectIdWriter;
663         WritableObjectId objectId = provider.findObjectId(bean, w.generator);
664         // If possible, write as id already
665         if (objectId.writeAsId(gen, provider, w)) {
666             return;
667         }
668         // If not, need to inject the id:
669         Object id = objectId.generateId(bean);
670         if (w.alwaysAsId) {
671             w.serializer.serialize(id, gen, provider);
672             return;
673         }
674         if (startEndObject) {
675             gen.writeStartObject(bean);
676         }
677         objectId.writeAsField(gen, provider, w);
678         if (_propertyFilterId != null) {
679             serializeFieldsFiltered(bean, gen, provider);
680         } else {
681             serializeFields(bean, gen, provider);
682         }
683         if (startEndObject) {
684             gen.writeEndObject();
685         }
686     }
687 
_serializeWithObjectId(Object bean, JsonGenerator gen, SerializerProvider provider, TypeSerializer typeSer)688     protected final void _serializeWithObjectId(Object bean, JsonGenerator gen, SerializerProvider provider,
689             TypeSerializer typeSer) throws IOException
690     {
691         final ObjectIdWriter w = _objectIdWriter;
692         WritableObjectId objectId = provider.findObjectId(bean, w.generator);
693         // If possible, write as id already
694         if (objectId.writeAsId(gen, provider, w)) {
695             return;
696         }
697         // If not, need to inject the id:
698         Object id = objectId.generateId(bean);
699         if (w.alwaysAsId) {
700             w.serializer.serialize(id, gen, provider);
701             return;
702         }
703         _serializeObjectId(bean, gen, provider, typeSer, objectId);
704     }
705 
_serializeObjectId(Object bean, JsonGenerator g, SerializerProvider provider, TypeSerializer typeSer, WritableObjectId objectId)706     protected  void _serializeObjectId(Object bean, JsonGenerator g,
707             SerializerProvider provider,
708             TypeSerializer typeSer, WritableObjectId objectId) throws IOException
709     {
710         final ObjectIdWriter w = _objectIdWriter;
711         WritableTypeId typeIdDef = _typeIdDef(typeSer, bean, JsonToken.START_OBJECT);
712 
713         typeSer.writeTypePrefix(g, typeIdDef);
714         objectId.writeAsField(g, provider, w);
715         if (_propertyFilterId != null) {
716             serializeFieldsFiltered(bean, g, provider);
717         } else {
718             serializeFields(bean, g, provider);
719         }
720         typeSer.writeTypeSuffix(g, typeIdDef);
721     }
722 
723     /**
724      * @since 2.9
725      */
_typeIdDef(TypeSerializer typeSer, Object bean, JsonToken valueShape)726     protected final WritableTypeId _typeIdDef(TypeSerializer typeSer,
727             Object bean, JsonToken valueShape) {
728         if (_typeId == null) {
729             return typeSer.typeId(bean, valueShape);
730         }
731         Object typeId = _typeId.getValue(bean);
732         if (typeId == null) {
733             // 28-Jun-2017, tatu: Is this really needed? Unchanged from 2.8 but...
734             typeId = "";
735         }
736         return typeSer.typeId(bean, valueShape, typeId);
737     }
738 
739     @Deprecated // since 2.9
_customTypeId(Object bean)740     protected final String _customTypeId(Object bean)
741     {
742         final Object typeId = _typeId.getValue(bean);
743         if (typeId == null) {
744             return "";
745         }
746         return (typeId instanceof String) ? (String) typeId : typeId.toString();
747     }
748 
749     /*
750     /**********************************************************
751     /* Field serialization methods
752     /**********************************************************
753      */
754 
serializeFields(Object bean, JsonGenerator gen, SerializerProvider provider)755     protected void serializeFields(Object bean, JsonGenerator gen, SerializerProvider provider)
756         throws IOException
757     {
758         final BeanPropertyWriter[] props;
759         if (_filteredProps != null && provider.getActiveView() != null) {
760             props = _filteredProps;
761         } else {
762             props = _props;
763         }
764         int i = 0;
765         try {
766             for (final int len = props.length; i < len; ++i) {
767                 BeanPropertyWriter prop = props[i];
768                 if (prop != null) { // can have nulls in filtered list
769                     prop.serializeAsField(bean, gen, provider);
770                 }
771             }
772             if (_anyGetterWriter != null) {
773                 _anyGetterWriter.getAndSerialize(bean, gen, provider);
774             }
775         } catch (Exception e) {
776             String name = (i == props.length) ? "[anySetter]" : props[i].getName();
777             wrapAndThrow(provider, e, bean, name);
778         } catch (StackOverflowError e) {
779             // 04-Sep-2009, tatu: Dealing with this is tricky, since we don't have many
780             //   stack frames to spare... just one or two; can't make many calls.
781 
782             // 10-Dec-2015, tatu: and due to above, avoid "from" method, call ctor directly:
783             //JsonMappingException mapE = JsonMappingException.from(gen, "Infinite recursion (StackOverflowError)", e);
784             JsonMappingException mapE = new JsonMappingException(gen, "Infinite recursion (StackOverflowError)", e);
785 
786              String name = (i == props.length) ? "[anySetter]" : props[i].getName();
787             mapE.prependPath(new JsonMappingException.Reference(bean, name));
788             throw mapE;
789         }
790     }
791 
792     /**
793      * Alternative serialization method that gets called when there is a
794      * {@link PropertyFilter} that needs to be called to determine
795      * which properties are to be serialized (and possibly how)
796      */
serializeFieldsFiltered(Object bean, JsonGenerator gen, SerializerProvider provider)797     protected void serializeFieldsFiltered(Object bean, JsonGenerator gen,
798             SerializerProvider provider)
799         throws IOException, JsonGenerationException
800     {
801         /* note: almost verbatim copy of "serializeFields"; copied (instead of merged)
802          * so that old method need not add check for existence of filter.
803          */
804         final BeanPropertyWriter[] props;
805         if (_filteredProps != null && provider.getActiveView() != null) {
806             props = _filteredProps;
807         } else {
808             props = _props;
809         }
810         final PropertyFilter filter = findPropertyFilter(provider, _propertyFilterId, bean);
811         // better also allow missing filter actually..
812         if (filter == null) {
813             serializeFields(bean, gen, provider);
814             return;
815         }
816         int i = 0;
817         try {
818             for (final int len = props.length; i < len; ++i) {
819                 BeanPropertyWriter prop = props[i];
820                 if (prop != null) { // can have nulls in filtered list
821                     filter.serializeAsField(bean, gen, provider, prop);
822                 }
823             }
824             if (_anyGetterWriter != null) {
825                 _anyGetterWriter.getAndFilter(bean, gen, provider, filter);
826             }
827         } catch (Exception e) {
828             String name = (i == props.length) ? "[anySetter]" : props[i].getName();
829             wrapAndThrow(provider, e, bean, name);
830         } catch (StackOverflowError e) {
831             // Minimize call depth since we are close to fail:
832             //JsonMappingException mapE = JsonMappingException.from(gen, "Infinite recursion (StackOverflowError)", e);
833             JsonMappingException mapE = new JsonMappingException(gen, "Infinite recursion (StackOverflowError)", e);
834             String name = (i == props.length) ? "[anySetter]" : props[i].getName();
835             mapE.prependPath(new JsonMappingException.Reference(bean, name));
836             throw mapE;
837         }
838     }
839 
840     @Deprecated
841     @Override
getSchema(SerializerProvider provider, Type typeHint)842     public JsonNode getSchema(SerializerProvider provider, Type typeHint)
843         throws JsonMappingException
844     {
845         ObjectNode o = createSchemaNode("object", true);
846         // [JACKSON-813]: Add optional JSON Schema id attribute, if found
847         // NOTE: not optimal, does NOT go through AnnotationIntrospector etc:
848         JsonSerializableSchema ann = _handledType.getAnnotation(JsonSerializableSchema.class);
849         if (ann != null) {
850             String id = ann.id();
851             if (id != null && id.length() > 0) {
852                 o.put("id", id);
853             }
854         }
855 
856         //todo: should the classname go in the title?
857         //o.put("title", _className);
858         ObjectNode propertiesNode = o.objectNode();
859         final PropertyFilter filter;
860         if (_propertyFilterId != null) {
861             filter = findPropertyFilter(provider, _propertyFilterId, null);
862         } else {
863             filter = null;
864         }
865 
866         for (int i = 0; i < _props.length; i++) {
867             BeanPropertyWriter prop = _props[i];
868             if (filter == null) {
869                 prop.depositSchemaProperty(propertiesNode, provider);
870             } else {
871                 filter.depositSchemaProperty(prop, propertiesNode, provider);
872             }
873 
874         }
875         o.set("properties", propertiesNode);
876         return o;
877     }
878 
879     @Override
acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)880     public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
881         throws JsonMappingException
882     {
883         //deposit your output format
884         if (visitor == null) {
885             return;
886         }
887         JsonObjectFormatVisitor objectVisitor = visitor.expectObjectFormat(typeHint);
888         if (objectVisitor == null) {
889             return;
890         }
891         final SerializerProvider provider = visitor.getProvider();
892         if (_propertyFilterId != null) {
893             PropertyFilter filter = findPropertyFilter(visitor.getProvider(),
894                     _propertyFilterId, null);
895             for (int i = 0, end = _props.length; i < end; ++i) {
896                 filter.depositSchemaProperty(_props[i], objectVisitor, provider);
897             }
898         } else {
899             Class<?> view = ((_filteredProps == null) || (provider == null))
900                     ? null : provider.getActiveView();
901             final BeanPropertyWriter[] props;
902             if (view != null) {
903                 props = _filteredProps;
904             } else {
905                 props = _props;
906             }
907 
908             for (int i = 0, end = props.length; i < end; ++i) {
909                 BeanPropertyWriter prop = props[i];
910                 if (prop != null) { // may be filtered out unconditionally
911                     prop.depositSchemaProperty(objectVisitor, provider);
912                 }
913             }
914         }
915     }
916 }
917