1 package com.fasterxml.jackson.databind.ser;
2
3 import java.util.*;
4
5 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
6 import com.fasterxml.jackson.annotation.ObjectIdGenerator;
7 import com.fasterxml.jackson.annotation.ObjectIdGenerators;
8 import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
9
10 import com.fasterxml.jackson.databind.*;
11 import com.fasterxml.jackson.databind.cfg.SerializerFactoryConfig;
12 import com.fasterxml.jackson.databind.introspect.*;
13 import com.fasterxml.jackson.databind.jsontype.NamedType;
14 import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
15 import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
16 import com.fasterxml.jackson.databind.ser.impl.FilteredBeanPropertyWriter;
17 import com.fasterxml.jackson.databind.ser.impl.ObjectIdWriter;
18 import com.fasterxml.jackson.databind.ser.impl.PropertyBasedObjectIdGenerator;
19 import com.fasterxml.jackson.databind.ser.std.MapSerializer;
20 import com.fasterxml.jackson.databind.ser.std.StdDelegatingSerializer;
21 import com.fasterxml.jackson.databind.type.ReferenceType;
22 import com.fasterxml.jackson.databind.util.ClassUtil;
23 import com.fasterxml.jackson.databind.util.Converter;
24
25 /**
26  * Factory class that can provide serializers for any regular Java beans
27  * (as defined by "having at least one get method recognizable as bean
28  * accessor" -- where {@link Object#getClass} does not count);
29  * as well as for "standard" JDK types. Latter is achieved
30  * by delegating calls to {@link BasicSerializerFactory} 
31  * to find serializers both for "standard" JDK types (and in some cases,
32  * sub-classes as is the case for collection classes like
33  * {@link java.util.List}s and {@link java.util.Map}s) and bean (value)
34  * classes.
35  *<p>
36  * Note about delegating calls to {@link BasicSerializerFactory}:
37  * although it would be nicer to use linear delegation
38  * for construction (to essentially dispatch all calls first to the
39  * underlying {@link BasicSerializerFactory}; or alternatively after
40  * failing to provide bean-based serializer}, there is a problem:
41  * priority levels for detecting standard types are mixed. That is,
42  * we want to check if a type is a bean after some of "standard" JDK
43  * types, but before the rest.
44  * As a result, "mixed" delegation used, and calls are NOT done using
45  * regular {@link SerializerFactory} interface but rather via
46  * direct calls to {@link BasicSerializerFactory}.
47  *<p>
48  * Finally, since all caching is handled by the serializer provider
49  * (not factory) and there is no configurability, this
50  * factory is stateless.
51  * This means that a global singleton instance can be used.
52  */

53 public class BeanSerializerFactory
54     extends BasicSerializerFactory
55     implements java.io.Serializable // since 2.1
56 {
57     private static final long serialVersionUID = 1;
58
59     /**
60      * Like {@link BasicSerializerFactory}, this factory is stateless, and
61      * thus a single shared global (== singleton) instance can be used
62      * without thread-safety issues.
63      */

64     public final static BeanSerializerFactory instance = new BeanSerializerFactory(null);
65
66     /*
67     /**********************************************************
68     /* Life-cycle: creation, configuration
69     /**********************************************************
70      */

71
72     /**
73      * Constructor for creating instances with specified configuration.
74      */

75     protected BeanSerializerFactory(SerializerFactoryConfig config)
76     {
77         super(config);
78     }
79     
80     /**
81      * Method used by module registration functionality, to attach additional
82      * serializer providers into this serializer factory. This is typically
83      * handled by constructing a new instance with additional serializers,
84      * to ensure thread-safe access.
85      */

86     @Override
87     public SerializerFactory withConfig(SerializerFactoryConfig config)
88     {
89         if (_factoryConfig == config) {
90             return this;
91         }
92         /* 22-Nov-2010, tatu: Handling of subtypes is tricky if we do immutable-with-copy-ctor;
93          *    and we pretty much have to here either choose between losing subtype instance
94          *    when registering additional serializers, or losing serializers.
95          *    Instead, let's actually just throw an error if this method is called when subtype
96          *    has not properly overridden this method; this to indicate problem as soon as possible.
97          */

98         if (getClass() != BeanSerializerFactory.class) {
99             throw new IllegalStateException("Subtype of BeanSerializerFactory ("+getClass().getName()
100                     +") has not properly overridden method 'withAdditionalSerializers': cannot instantiate subtype with "
101                     +"additional serializer definitions");
102         }
103         return new BeanSerializerFactory(config);
104     }
105
106     @Override
107     protected Iterable<Serializers> customSerializers() {
108         return _factoryConfig.serializers();
109     }
110
111     /*
112     /**********************************************************
113     /* SerializerFactory impl
114     /**********************************************************
115      */

116
117     /**
118      * Main serializer constructor method. We will have to be careful
119      * with respect to ordering of various method calls: essentially
120      * we want to reliably figure out which classes are standard types,
121      * and which are beans. The problem is that some bean Classes may
122      * implement standard interfaces (say, {@link java.lang.Iterable}.
123      *<p>
124      * Note: sub-classes may choose to complete replace implementation,
125      * if they want to alter priority of serializer lookups.
126      */

127     @Override
128     @SuppressWarnings("unchecked")
129     public JsonSerializer<Object> createSerializer(SerializerProvider prov,
130             JavaType origType)
131         throws JsonMappingException
132     {
133         // Very first thing, let's check if there is explicit serializer annotation:
134         final SerializationConfig config = prov.getConfig();
135         BeanDescription beanDesc = config.introspect(origType);
136         JsonSerializer<?> ser = findSerializerFromAnnotation(prov, beanDesc.getClassInfo());
137         if (ser != null) {
138             return (JsonSerializer<Object>) ser;
139         }
140         boolean staticTyping;
141         // Next: we may have annotations that further indicate actual type to use (a super type)
142         final AnnotationIntrospector intr = config.getAnnotationIntrospector();
143         JavaType type;
144
145         if (intr == null) {
146             type = origType;
147         } else {
148             try {
149                 type = intr.refineSerializationType(config, beanDesc.getClassInfo(), origType);
150             } catch (JsonMappingException e) {
151                 return prov.reportBadTypeDefinition(beanDesc, e.getMessage());
152             }
153         }
154         if (type == origType) { // no changes, won't force static typing
155             staticTyping = false;
156         } else { // changes; assume static typing; plus, need to re-introspect if class differs
157             staticTyping = true;
158             if (!type.hasRawClass(origType.getRawClass())) {
159                 beanDesc = config.introspect(type);
160             }
161         }
162         // Slight detour: do we have a Converter to consider?
163         Converter<Object,Object> conv = beanDesc.findSerializationConverter();
164         if (conv == null) { // no, simple
165             return (JsonSerializer<Object>) _createSerializer2(prov, type, beanDesc, staticTyping);
166         }
167         JavaType delegateType = conv.getOutputType(prov.getTypeFactory());
168         
169         // One more twist, as per [databind#288]; probably need to get new BeanDesc
170         if (!delegateType.hasRawClass(type.getRawClass())) {
171             beanDesc = config.introspect(delegateType);
172             // [#359]: explicitly check (again) for @JsonSerializer...
173             ser = findSerializerFromAnnotation(prov, beanDesc.getClassInfo());
174         }
175         // [databind#731]: Should skip if nominally java.lang.Object
176         if (ser == null && !delegateType.isJavaLangObject()) {
177             ser = _createSerializer2(prov, delegateType, beanDesc, true);
178         }
179         return new StdDelegatingSerializer(conv, delegateType, ser);
180     }
181
182     protected JsonSerializer<?> _createSerializer2(SerializerProvider prov,
183             JavaType type, BeanDescription beanDesc, boolean staticTyping)
184         throws JsonMappingException
185     {
186         JsonSerializer<?> ser = null;
187         final SerializationConfig config = prov.getConfig();
188         
189         // Container types differ from non-container types
190         // (note: called method checks for module-provided serializers)
191         if (type.isContainerType()) {
192             if (!staticTyping) {
193                 staticTyping = usesStaticTyping(config, beanDesc, null);
194             }
195             // 03-Aug-2012, tatu: As per [databind#40], may require POJO serializer...
196             ser =  buildContainerSerializer(prov, type, beanDesc, staticTyping);
197             // Will return right away, since called method does post-processing:
198             if (ser != null) {
199                 return ser;
200             }
201         } else {
202             if (type.isReferenceType()) {
203                 ser = findReferenceSerializer(prov, (ReferenceType) type, beanDesc, staticTyping);
204             } else {
205                 // Modules may provide serializers of POJO types:
206                 for (Serializers serializers : customSerializers()) {
207                     ser = serializers.findSerializer(config, type, beanDesc);
208                     if (ser != null) {
209                         break;
210                     }
211                 }
212             }
213             // 25-Jun-2015, tatu: Then JsonSerializable, @JsonValue etc. NOTE! Prior to 2.6,
214             //    this call was BEFORE custom serializer lookup, which was wrong.
215             if (ser == null) {
216                 ser = findSerializerByAnnotations(prov, type, beanDesc);
217             }
218         }
219         
220         if (ser == null) {
221             // Otherwise, we will check "primary types"; both marker types that
222             // indicate specific handling (JsonSerializable), or main types that have
223             // precedence over container types
224             ser = findSerializerByLookup(type, config, beanDesc, staticTyping);
225             if (ser == null) {
226                 ser = findSerializerByPrimaryType(prov, type, beanDesc, staticTyping);
227                 if (ser == null) {
228                     // And this is where this class comes in: if type is not a
229                     // known "primary JDK type", perhaps it's a bean? We can still
230                     // get a nullif we can't find a single suitable bean property.
231                     ser = findBeanOrAddOnSerializer(prov, type, beanDesc, staticTyping);
232                     // 18-Sep-2014, tatu: Actually, as per [jackson-databind#539], need to get
233                     //   'unknown' serializer assigned earlier, here, so that it gets properly
234                     //   post-processed
235                     if (ser == null) {
236                         ser = prov.getUnknownTypeSerializer(beanDesc.getBeanClass());
237                     }
238                 }
239             }
240         }
241         if (ser != null) {
242             // [databind#120]: Allow post-processing
243             if (_factoryConfig.hasSerializerModifiers()) {
244                 for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) {
245                     ser = mod.modifySerializer(config, beanDesc, ser);
246                 }
247             }
248         }
249         return ser;
250     }
251     
252     /*
253     /**********************************************************
254     /* Other public methods that are not part of
255     /* JsonSerializerFactory API
256     /**********************************************************
257      */

258
259     @Deprecated // since 2.10
260     public JsonSerializer<Object> findBeanSerializer(SerializerProvider prov, JavaType type,
261             BeanDescription beanDesc)
262         throws JsonMappingException
263     {
264         return findBeanOrAddOnSerializer(prov, type, beanDesc, prov.isEnabled(MapperFeature.USE_STATIC_TYPING));
265     }
266
267     /**
268      * Method that will try to construct a {@link BeanSerializer} for
269      * given class if at least one property is found, OR, if not,
270      * one of add-on types.
271      *<p>
272      * NOTE: behavior changed a bit
273      */

274     public JsonSerializer<Object> findBeanOrAddOnSerializer(SerializerProvider prov, JavaType type,
275             BeanDescription beanDesc, boolean staticTyping)
276         throws JsonMappingException
277     {
278         // First things first: we know some types are not beans...
279         if (!isPotentialBeanType(type.getRawClass())) {
280             // 03-Aug-2012, tatu: Except we do need to allow serializers for Enums,
281             //   as per [databind#24], [databind#2576]
282             if (!ClassUtil.isEnumType(type.getRawClass())) {
283                 return null;
284             }
285         }
286         return constructBeanOrAddOnSerializer(prov, type, beanDesc, staticTyping);
287     }
288
289     /**
290      * Method called to create a type information serializer for values of given
291      * non-container property
292      * if one is needed. If not needed (no polymorphic handling configured), should
293      * return null.
294      *
295      * @param baseType Declared type to use as the base type for type information serializer
296      * 
297      * @return Type serializer to use for property values, if one is needed; null if not.
298      */

299     public TypeSerializer findPropertyTypeSerializer(JavaType baseType,
300             SerializationConfig config, AnnotatedMember accessor)
301         throws JsonMappingException
302     {
303         AnnotationIntrospector ai = config.getAnnotationIntrospector();
304         TypeResolverBuilder<?> b = ai.findPropertyTypeResolver(config, accessor, baseType);        
305         TypeSerializer typeSer;
306
307         // Defaulting: if no annotations on member, check value class
308         if (b == null) {
309             typeSer = createTypeSerializer(config, baseType);
310         } else {
311             Collection<NamedType> subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByClass(
312                     config, accessor, baseType);
313             typeSer = b.buildTypeSerializer(config, baseType, subtypes);
314         }
315         return typeSer;
316     }
317
318     /**
319      * Method called to create a type information serializer for values of given
320      * container property
321      * if one is needed. If not needed (no polymorphic handling configured), should
322      * return null.
323      *
324      * @param containerType Declared type of the container to use as the base type for type information serializer
325      * 
326      * @return Type serializer to use for property value contents, if one is needed; null if not.
327      */
    
328     public TypeSerializer findPropertyContentTypeSerializer(JavaType containerType,
329             SerializationConfig config, AnnotatedMember accessor)
330         throws JsonMappingException
331     {
332         JavaType contentType = containerType.getContentType();
333         AnnotationIntrospector ai = config.getAnnotationIntrospector();
334         TypeResolverBuilder<?> b = ai.findPropertyContentTypeResolver(config, accessor, containerType);        
335         TypeSerializer typeSer;
336
337         // Defaulting: if no annotations on member, check value class
338         if (b == null) {
339             typeSer = createTypeSerializer(config, contentType);
340         } else {
341             Collection<NamedType> subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByClass(config,
342                     accessor, contentType);
343             typeSer = b.buildTypeSerializer(config, contentType, subtypes);
344         }
345         return typeSer;
346     }
347
348     /*
349     /**********************************************************
350     /* Overridable non-public factory methods
351     /**********************************************************
352      */

353
354     @Deprecated // since 2.10
355     protected JsonSerializer<Object> constructBeanSerializer(SerializerProvider prov,
356             BeanDescription beanDesc)
357         throws JsonMappingException
358     {
359         return constructBeanOrAddOnSerializer(prov, beanDesc.getType(), beanDesc, prov.isEnabled(MapperFeature.USE_STATIC_TYPING));
360     }
361
362     /**
363      * Method called to construct serializer for serializing specified bean type if
364      * (but only if, as of 2.10), at least one property is found.
365      * 
366      * @since 2.10
367      */

368     @SuppressWarnings("unchecked")
369     protected JsonSerializer<Object> constructBeanOrAddOnSerializer(SerializerProvider prov,
370             JavaType type, BeanDescription beanDesc, boolean staticTyping)
371         throws JsonMappingException
372     {
373         // 13-Oct-2010, tatu: quick sanity check: never try to create bean serializer for plain Object
374         // 05-Jul-2012, tatu: ... but we should be able to just return "unknown type" serializer, right?
375         if (beanDesc.getBeanClass() == Object.class) {
376             return prov.getUnknownTypeSerializer(Object.class);
377 //            throw new IllegalArgumentException("Cannot create bean serializer for Object.class");
378         }
379         final SerializationConfig config = prov.getConfig();
380         BeanSerializerBuilder builder = constructBeanSerializerBuilder(beanDesc);
381         builder.setConfig(config);
382
383         // First: any detectable (auto-detect, annotations) properties to serialize?
384         List<BeanPropertyWriter> props = findBeanProperties(prov, beanDesc, builder);
385         if (props == null) {
386             props = new ArrayList<BeanPropertyWriter>();
387         } else {
388             props = removeOverlappingTypeIds(prov, beanDesc, builder, props);
389         }
390         
391         // [databind#638]: Allow injection of "virtual" properties:
392         prov.getAnnotationIntrospector().findAndAddVirtualProperties(config, beanDesc.getClassInfo(), props);
393
394         // [JACKSON-440] Need to allow modification bean properties to serialize:
395         if (_factoryConfig.hasSerializerModifiers()) {
396             for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) {
397                 props = mod.changeProperties(config, beanDesc, props);
398             }
399         }
400
401         // Any properties to suppress?
402         props = filterBeanProperties(config, beanDesc, props);
403
404         // Need to allow reordering of properties to serialize
405         if (_factoryConfig.hasSerializerModifiers()) {
406             for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) {
407                 props = mod.orderProperties(config, beanDesc, props);
408             }
409         }
410
411         // And if Object Id is needed, some preparation for that as well: better
412         // do before view handling, mostly for the custom id case which needs
413         // access to a property
414         builder.setObjectIdWriter(constructObjectIdHandler(prov, beanDesc, props));
415         
416         builder.setProperties(props);
417         builder.setFilterId(findFilterId(config, beanDesc));
418
419         AnnotatedMember anyGetter = beanDesc.findAnyGetter();
420         if (anyGetter != null) {
421             JavaType anyType = anyGetter.getType();
422             // copied from BasicSerializerFactory.buildMapSerializer():
423             JavaType valueType = anyType.getContentType();
424             TypeSerializer typeSer = createTypeSerializer(config, valueType);
425             // last 2 nulls; don't know key, value serializers (yet)
426             // 23-Feb-2015, tatu: As per [databind#705], need to support custom serializers
427             JsonSerializer<?> anySer = findSerializerFromAnnotation(prov, anyGetter);
428             if (anySer == null) {
429                 // TODO: support '@JsonIgnoreProperties' with any setter?
430                 anySer = MapSerializer.construct(/* ignored props*/ (Set<String>) null,
431                         anyType, config.isEnabled(MapperFeature.USE_STATIC_TYPING),
432                         typeSer, nullnull/*filterId*/ null);
433             }
434             // TODO: can we find full PropertyName?
435             PropertyName name = PropertyName.construct(anyGetter.getName());
436             BeanProperty.Std anyProp = new BeanProperty.Std(name, valueType, null,
437                     anyGetter, PropertyMetadata.STD_OPTIONAL);
438             builder.setAnyGetter(new AnyGetterWriter(anyProp, anyGetter, anySer));
439         }
440         // Next: need to gather view information, if any:
441         processViews(config, builder);
442
443         // Finally: let interested parties mess with the result bit more...
444         if (_factoryConfig.hasSerializerModifiers()) {
445             for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) {
446                 builder = mod.updateBuilder(config, beanDesc, builder);
447             }
448         }
449
450         JsonSerializer<Object> ser = null;
451         try {
452             ser = (JsonSerializer<Object>) builder.build();
453         } catch (RuntimeException e) {
454             return prov.reportBadTypeDefinition(beanDesc, "Failed to construct BeanSerializer for %s: (%s) %s",
455                     beanDesc.getType(), e.getClass().getName(), e.getMessage());
456         }
457         if (ser == null) {
458             // 06-Aug-2019, tatu: As per [databind#2390], we need to check for add-ons here,
459             //    before considering fallbacks
460             ser = (JsonSerializer<Object>) findSerializerByAddonType(config, type, beanDesc, staticTyping);
461             if (ser == null) {
462                 // If we get this far, there were no properties found, so no regular BeanSerializer
463                 // would be constructed. But, couple of exceptions.
464                 // First: if there are known annotations, just create 'empty bean' serializer
465                 if (beanDesc.hasKnownClassAnnotations()) {
466                     return builder.createDummy();
467                 }
468             }
469         }
470         return ser;
471     }
472
473     protected ObjectIdWriter constructObjectIdHandler(SerializerProvider prov,
474             BeanDescription beanDesc, List<BeanPropertyWriter> props)
475         throws JsonMappingException
476     {
477         ObjectIdInfo objectIdInfo = beanDesc.getObjectIdInfo();
478         if (objectIdInfo == null) {
479             return null;
480         }
481         ObjectIdGenerator<?> gen;
482         Class<?> implClass = objectIdInfo.getGeneratorType();
483
484         // Just one special case: Property-based generator is trickier
485         if (implClass == ObjectIdGenerators.PropertyGenerator.class) { // most special one, needs extra work
486             String propName = objectIdInfo.getPropertyName().getSimpleName();
487             BeanPropertyWriter idProp = null;
488
489             for (int i = 0, len = props.size() ;; ++i) {
490                 if (i == len) {
491                     throw new IllegalArgumentException("Invalid Object Id definition for "+beanDesc.getBeanClass().getName()
492                             +": cannot find property with name '"+propName+"'");
493                 }
494                 BeanPropertyWriter prop = props.get(i);
495                 if (propName.equals(prop.getName())) {
496                     idProp = prop;
497                     // Let's force it to be the first property to output
498                     // (although it may still get rearranged etc)
499                     if (i > 0) {
500                         props.remove(i);
501                         props.add(0, idProp);
502                     }
503                     break;
504                 }
505             }
506             JavaType idType = idProp.getType();
507             gen = new PropertyBasedObjectIdGenerator(objectIdInfo, idProp);
508             // one more thing: must ensure that ObjectIdWriter does not actually write the value:
509             return ObjectIdWriter.construct(idType, (PropertyName) null, gen, objectIdInfo.getAlwaysAsId());
510             
511         } 
512         // other types are simpler
513         JavaType type = prov.constructType(implClass);
514         // Could require type to be passed explicitly, but we should be able to find it too:
515         JavaType idType = prov.getTypeFactory().findTypeParameters(type, ObjectIdGenerator.class)[0];
516         gen = prov.objectIdGeneratorInstance(beanDesc.getClassInfo(), objectIdInfo);
517         return ObjectIdWriter.construct(idType, objectIdInfo.getPropertyName(), gen,
518                 objectIdInfo.getAlwaysAsId());
519     }
520
521     /**
522      * Method called to construct a filtered writer, for given view
523      * definitions. Default implementation constructs filter that checks
524      * active view type to views property is to be included in.
525      */

526     protected BeanPropertyWriter constructFilteredBeanWriter(BeanPropertyWriter writer,
527             Class<?>[] inViews)
528     {
529         return FilteredBeanPropertyWriter.constructViewBased(writer, inViews);
530     }
531     
532     protected PropertyBuilder constructPropertyBuilder(SerializationConfig config,
533             BeanDescription beanDesc)
534     {
535         return new PropertyBuilder(config, beanDesc);
536     }
537
538     protected BeanSerializerBuilder constructBeanSerializerBuilder(BeanDescription beanDesc) {
539         return new BeanSerializerBuilder(beanDesc);
540     }
541     
542     /*
543     /**********************************************************
544     /* Overridable non-public introspection methods
545     /**********************************************************
546      */

547     
548     /**
549      * Helper method used to skip processing for types that we know
550      * cannot be (i.e. are never consider to be) beans: 
551      * things like primitives, Arrays, Enums, and proxy types.
552      *<p>
553      * Note that usually we shouldn't really be getting these sort of
554      * types anyway; but better safe than sorry.
555      */

556     protected boolean isPotentialBeanType(Class<?> type)
557     {
558         return (ClassUtil.canBeABeanType(type) == null) && !ClassUtil.isProxyType(type);
559     }
560
561     /**
562      * Method used to collect all actual serializable properties.
563      * Can be overridden to implement custom detection schemes.
564      */

565     protected List<BeanPropertyWriter> findBeanProperties(SerializerProvider prov,
566             BeanDescription beanDesc, BeanSerializerBuilder builder)
567         throws JsonMappingException
568     {
569         List<BeanPropertyDefinition> properties = beanDesc.findProperties();
570         final SerializationConfig config = prov.getConfig();
571
572         // ignore specified types
573         removeIgnorableTypes(config, beanDesc, properties);
574         
575         // and possibly remove ones without matching mutator...
576         if (config.isEnabled(MapperFeature.REQUIRE_SETTERS_FOR_GETTERS)) {
577             removeSetterlessGetters(config, beanDesc, properties);
578         }
579         
580         // nothing? can't proceed (caller may or may not throw an exception)
581         if (properties.isEmpty()) {
582             return null;
583         }
584         // null is for value type serializer, which we don't have access to from here (ditto for bean prop)
585         boolean staticTyping = usesStaticTyping(config, beanDesc, null);
586         PropertyBuilder pb = constructPropertyBuilder(config, beanDesc);
587         
588         ArrayList<BeanPropertyWriter> result = new ArrayList<BeanPropertyWriter>(properties.size());
589         for (BeanPropertyDefinition property : properties) {
590             final AnnotatedMember accessor = property.getAccessor();
591             // Type id? Requires special handling:
592             if (property.isTypeId()) {
593                 if (accessor != null) {
594                     builder.setTypeId(accessor);
595                 }
596                 continue;
597             }
598             // suppress writing of back references
599             AnnotationIntrospector.ReferenceProperty refType = property.findReferenceType();
600             if (refType != null && refType.isBackReference()) {
601                 continue;
602             }
603             if (accessor instanceof AnnotatedMethod) {
604                 result.add(_constructWriter(prov, property, pb, staticTyping, (AnnotatedMethod) accessor));
605             } else {
606                 result.add(_constructWriter(prov, property, pb, staticTyping, (AnnotatedField) accessor));
607             }
608         }
609         return result;
610     }
611
612     /*
613     /**********************************************************
614     /* Overridable non-public methods for manipulating bean properties
615     /**********************************************************
616      */

617     
618     /**
619      * Overridable method that can filter out properties. Default implementation
620      * checks annotations class may have.
621      */

622     protected List<BeanPropertyWriter> filterBeanProperties(SerializationConfig config,
623             BeanDescription beanDesc, List<BeanPropertyWriter> props)
624     {
625         // 01-May-2016, tatu: Which base type to use here gets tricky, since
626         //   it may often make most sense to use general type for overrides,
627         //   but what we have here may be more specific impl type. But for now
628         //   just use it as is.
629         JsonIgnoreProperties.Value ignorals = config.getDefaultPropertyIgnorals(beanDesc.getBeanClass(),
630                 beanDesc.getClassInfo());
631         if (ignorals != null) {
632             Set<String> ignored = ignorals.findIgnoredForSerialization();
633             if (!ignored.isEmpty()) {
634                 Iterator<BeanPropertyWriter> it = props.iterator();
635                 while (it.hasNext()) {
636                     if (ignored.contains(it.next().getName())) {
637                         it.remove();
638                     }
639                 }
640             }
641         }
642         return props;
643     }
644
645     /**
646      * Method called to handle view information for constructed serializer,
647      * based on bean property writers.
648      *<p>
649      * Note that this method is designed to be overridden by sub-classes
650      * if they want to provide custom view handling. As such it is not
651      * considered an internal implementation detail, and will be supported
652      * as part of API going forward.
653      */

654     protected void processViews(SerializationConfig config, BeanSerializerBuilder builder)
655     {
656         // whether non-annotated fields are included by default or not is configurable
657         List<BeanPropertyWriter> props = builder.getProperties();
658         boolean includeByDefault = config.isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION);
659         final int propCount = props.size();
660         int viewsFound = 0;
661         BeanPropertyWriter[] filtered = new BeanPropertyWriter[propCount];
662         // Simple: view information is stored within individual writers, need to combine:
663         for (int i = 0; i < propCount; ++i) {
664             BeanPropertyWriter bpw = props.get(i);
665             Class<?>[] views = bpw.getViews();
666             if (views == null
667                     // [databind#2311]: sometimes we add empty array
668                     || views.length == 0) { // no view info? include or exclude by default?
669                 if (includeByDefault) {
670                     filtered[i] = bpw;
671                 }
672             } else {
673                 ++viewsFound;
674                 filtered[i] = constructFilteredBeanWriter(bpw, views);
675             }
676         }
677         // minor optimization: if no view info, include-by-default, can leave out filtering info altogether:
678         if (includeByDefault && viewsFound == 0) {
679             return;
680         }
681         builder.setFilteredProperties(filtered);
682     }
683
684     /**
685      * Method that will apply by-type limitations (as per [JACKSON-429]);
686      * by default this is based on {@link com.fasterxml.jackson.annotation.JsonIgnoreType}
687      * annotation but can be supplied by module-provided introspectors too.
688      * Starting with 2.8 there are also "Config overrides" to consider.
689      */

690     protected void removeIgnorableTypes(SerializationConfig config, BeanDescription beanDesc,
691             List<BeanPropertyDefinition> properties)
692     {
693         AnnotationIntrospector intr = config.getAnnotationIntrospector();
694         HashMap<Class<?>,Boolean> ignores = new HashMap<Class<?>,Boolean>();
695         Iterator<BeanPropertyDefinition> it = properties.iterator();
696         while (it.hasNext()) {
697             BeanPropertyDefinition property = it.next();
698             AnnotatedMember accessor = property.getAccessor();
699             /* 22-Oct-2016, tatu: Looks like this removal is an important part of
700              *    processing, as taking it out will result in a few test failures...
701              *    But should probably be done somewhere else, not here?
702              */

703             if (accessor == null) {
704                 it.remove();
705                 continue;
706             }
707             Class<?> type = property.getRawPrimaryType();
708             Boolean result = ignores.get(type);
709             if (result == null) {
710                 // 21-Apr-2016, tatu: For 2.8, can specify config overrides
711                 result = config.getConfigOverride(type).getIsIgnoredType();
712                 if (result == null) {
713                     BeanDescription desc = config.introspectClassAnnotations(type);
714                     AnnotatedClass ac = desc.getClassInfo();
715                     result = intr.isIgnorableType(ac);
716                     // default to false, non-ignorable
717                     if (result == null) {
718                         result = Boolean.FALSE;
719                     }
720                 }
721                 ignores.put(type, result);
722             }
723             // lotsa work, and yes, it is ignorable type, so:
724             if (result.booleanValue()) {
725                 it.remove();
726             }
727         }
728     }
729
730     /**
731      * Helper method that will remove all properties that do not have a mutator.
732      */

733     protected void removeSetterlessGetters(SerializationConfig config, BeanDescription beanDesc,
734             List<BeanPropertyDefinition> properties)
735     {
736         Iterator<BeanPropertyDefinition> it = properties.iterator();
737         while (it.hasNext()) {
738             BeanPropertyDefinition property = it.next();
739             // one caveat: only remove implicit properties;
740             // explicitly annotated ones should remain
741             if (!property.couldDeserialize() && !property.isExplicitlyIncluded()) {
742                 it.remove();
743             }
744         }
745     }
746
747     /**
748      * Helper method called to ensure that we do not have "duplicate" type ids.
749      * Added to resolve [databind#222]
750      *
751      * @since 2.6
752      */

753     protected List<BeanPropertyWriter> removeOverlappingTypeIds(SerializerProvider prov,
754             BeanDescription beanDesc, BeanSerializerBuilder builder,
755             List<BeanPropertyWriter> props)
756     {
757         for (int i = 0, end = props.size(); i < end; ++i) {
758             BeanPropertyWriter bpw = props.get(i);
759             TypeSerializer td = bpw.getTypeSerializer();
760             if ((td == null) || (td.getTypeInclusion() != As.EXTERNAL_PROPERTY)) {
761                 continue;
762             }
763             String n = td.getPropertyName();
764             PropertyName typePropName = PropertyName.construct(n);
765
766             for (BeanPropertyWriter w2 : props) {
767                 if ((w2 != bpw) && w2.wouldConflictWithName(typePropName)) {
768                     bpw.assignTypeSerializer(null);
769                     break;
770                 }
771             }
772         }
773         return props;
774     }
775     
776     /*
777     /**********************************************************
778     /* Internal helper methods
779     /**********************************************************
780      */

781
782     /**
783      * Secondary helper method for constructing {@link BeanPropertyWriter} for
784      * given member (field or method).
785      */

786     protected BeanPropertyWriter _constructWriter(SerializerProvider prov,
787             BeanPropertyDefinition propDef,
788             PropertyBuilder pb, boolean staticTyping, AnnotatedMember accessor)
789         throws JsonMappingException
790     {
791         final PropertyName name = propDef.getFullName();
792         JavaType type = accessor.getType();
793         BeanProperty.Std property = new BeanProperty.Std(name, type, propDef.getWrapperName(),
794                 accessor, propDef.getMetadata());
795
796         // Does member specify a serializer? If so, let's use it.
797         JsonSerializer<?> annotatedSerializer = findSerializerFromAnnotation(prov,
798                 accessor);
799         // Unlike most other code paths, serializer produced
800         // here will NOT be resolved or contextualized, unless done here, so:
801         if (annotatedSerializer instanceof ResolvableSerializer) {
802             ((ResolvableSerializer) annotatedSerializer).resolve(prov);
803         }
804         // 05-Sep-2013, tatu: should be primary property serializer so:
805         annotatedSerializer = prov.handlePrimaryContextualization(annotatedSerializer, property);
806         // And how about polymorphic typing? First special to cover JAXB per-field settings:
807         TypeSerializer contentTypeSer = null;
808         // 16-Feb-2014, cgc: contentType serializers for collection-like and map-like types
809         if (type.isContainerType() || type.isReferenceType()) {
810             contentTypeSer = findPropertyContentTypeSerializer(type, prov.getConfig(), accessor);
811         }
812         // and if not JAXB collection/array with annotations, maybe regular type info?
813         TypeSerializer typeSer = findPropertyTypeSerializer(type, prov.getConfig(), accessor);
814         return pb.buildWriter(prov, propDef, type, annotatedSerializer,
815                         typeSer, contentTypeSer, accessor, staticTyping);
816     }
817 }
818