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 null, if 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, null, null, /*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