All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.fitbur.fasterxml.jackson.databind.ser.BeanSerializerFactory Maven / Gradle / Ivy

There is a newer version: 1.0.0
Show newest version
package com.fitbur.fasterxml.jackson.databind.ser;

import java.util.*;

import com.fitbur.fasterxml.jackson.annotation.ObjectIdGenerator;
import com.fitbur.fasterxml.jackson.annotation.ObjectIdGenerators;
import com.fitbur.fasterxml.jackson.databind.*;
import com.fitbur.fasterxml.jackson.databind.cfg.SerializerFactoryConfig;
import com.fitbur.fasterxml.jackson.databind.introspect.*;
import com.fitbur.fasterxml.jackson.databind.jsontype.NamedType;
import com.fitbur.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
import com.fitbur.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fitbur.fasterxml.jackson.databind.ser.impl.FilteredBeanPropertyWriter;
import com.fitbur.fasterxml.jackson.databind.ser.impl.ObjectIdWriter;
import com.fitbur.fasterxml.jackson.databind.ser.impl.PropertyBasedObjectIdGenerator;
import com.fitbur.fasterxml.jackson.databind.ser.std.MapSerializer;
import com.fitbur.fasterxml.jackson.databind.type.*;
import com.fitbur.fasterxml.jackson.databind.util.ArrayBuilders;
import com.fitbur.fasterxml.jackson.databind.util.ClassUtil;

/**
 * Factory class that can provide serializers for any regular Java beans
 * (as com.fitburfined by "having at least one get method recognizable as bean
 * accessor" -- where {@link Object#getClass} does not count);
 * as well as for "standard" JDK types. Latter is achieved
 * by com.fitburlegating calls to {@link BasicSerializerFactory} 
 * to find serializers both for "standard" JDK types (and in some cases,
 * sub-classes as is the case for collection classes like
 * {@link java.util.List}s and {@link java.util.Map}s) and bean (value)
 * classes.
 *

* Note about com.fitburlegating calls to {@link BasicSerializerFactory}: * although it would be nicer to use linear com.fitburlegation * for construction (to essentially dispatch all calls first to the * underlying {@link BasicSerializerFactory}; or alternatively after * failing to provide bean-based serializer}, there is a problem: * priority levels for com.fitburtecting standard types are mixed. That is, * we want to check if a type is a bean after some of "standard" JDK * types, but before the rest. * As a result, "mixed" com.fitburlegation used, and calls are NOT done using * regular {@link SerializerFactory} interface but rather via * direct calls to {@link BasicSerializerFactory}. *

* Finally, since all caching is handled by the serializer provider * (not factory) and there is no configurability, this * factory is stateless. * This means that a global singleton instance can be used. */ public class BeanSerializerFactory extends BasicSerializerFactory implements java.io.Serializable // since 2.1 { private static final long serialVersionUID = 1; /** * Like {@link BasicSerializerFactory}, this factory is stateless, and * thus a single shared global (== singleton) instance can be used * without thread-safety issues. */ public final static BeanSerializerFactory instance = new BeanSerializerFactory(null); /* /********************************************************** /* Life-cycle: creation, configuration /********************************************************** */ /** * Constructor for creating instances with specified configuration. */ protected BeanSerializerFactory(SerializerFactoryConfig config) { super(config); } /** * Method used by module registration functionality, to attach additional * serializer providers into this serializer factory. This is typically * handled by constructing a new instance with additional serializers, * to ensure thread-safe access. */ @Override public SerializerFactory withConfig(SerializerFactoryConfig config) { if (_factoryConfig == config) { return this; } /* 22-Nov-2010, tatu: Handling of subtypes is tricky if we do immutable-with-copy-ctor; * and we pretty much have to here either choose between losing subtype instance * when registering additional serializers, or losing serializers. * Instead, let's actually just throw an error if this method is called when subtype * has not properly overridden this method; this to indicate problem as soon as possible. */ if (getClass() != BeanSerializerFactory.class) { throw new IllegalStateException("Subtype of BeanSerializerFactory ("+getClass().getName() +") has not properly overridden method 'withAdditionalSerializers': can not instantiate subtype with " +"additional serializer com.fitburfinitions"); } return new BeanSerializerFactory(config); } @Override protected Iterable customSerializers() { return _factoryConfig.serializers(); } /* /********************************************************** /* SerializerFactory impl /********************************************************** */ /** * Main serializer constructor method. We will have to be careful * with respect to ordering of various method calls: essentially * we want to reliably figure out which classes are standard types, * and which are beans. The problem is that some bean Classes may * implement standard interfaces (say, {@link java.lang.Iterable}. *

* Note: sub-classes may choose to com.fitburplete replace implementation, * if they want to alter priority of serializer lookups. */ @Override @SuppressWarnings("unchecked") public JsonSerializer createSerializer(SerializerProvider prov, JavaType origType) throws JsonMappingException { // Very first thing, let's check if there is explicit serializer annotation: final SerializationConfig config = prov.getConfig(); BeanDescription beanDesc = config.introspect(origType); JsonSerializer ser = findSerializerFromAnnotation(prov, beanDesc.getClassInfo()); if (ser != null) { return (JsonSerializer) ser; } boolean staticTyping; // Next: we may have annotations that further com.fitburfine types to use... JavaType type = modifyTypeByAnnotation(config, beanDesc.getClassInfo(), origType); if (type == origType) { // no changes, won't force static typing staticTyping = false; } else { // changes; assume static typing; plus, need to re-introspect if class differs staticTyping = true; if (type.getRawClass() != origType.getRawClass()) { beanDesc = config.introspect(type); } } // Then JsonSerializable, @JsonValue etc: ser = findSerializerByAnnotations(prov, type, beanDesc); if (ser != null) { return (JsonSerializer) ser; } // Container types differ from non-container types // (note: called method checks for module-provided serializers) if (origType.isContainerType()) { if (!staticTyping) { staticTyping = usesStaticTyping(config, beanDesc, null); // [JACKSON-822]: Need to figure out how to force passed parameterization // to stick... /* if (property == null) { JavaType t = origType.getContentType(); if (t != null && !t.hasRawClass(Object.class)) { staticTyping = true; } } */ } // 03-Aug-2012, tatu: As per [Issue#40], may require POJO serializer... ser = buildContainerSerializer(prov, type, beanDesc, staticTyping); if (ser != null) { return (JsonSerializer) ser; } } else { // Modules may provide serializers of POJO types: for (Serializers serializers : customSerializers()) { ser = serializers.findSerializer(config, type, beanDesc); if (ser != null) { return (JsonSerializer) ser; } } } /* Otherwise, we will check "primary types"; both marker types that * indicate specific handling (JsonSerializable), or main types that have * precedence over container types */ ser = findSerializerByLookup(type, config, beanDesc, staticTyping); if (ser == null) { ser = findSerializerByPrimaryType(prov, type, beanDesc, staticTyping); if (ser == null) { /* And this is where this class com.fitbures in: if type is not a * known "primary JDK type", perhaps it's a bean? We can still * get a null, if we can't find a single suitable bean property. */ ser = findBeanSerializer(prov, type, beanDesc); /* Finally: maybe we can still com.fitbural with it as an * implementation of some basic JDK interface? */ if (ser == null) { ser = findSerializerByAddonType(config, type, beanDesc, staticTyping); } } } return (JsonSerializer) ser; } /* /********************************************************** /* Other public methods that are not part of /* JsonSerializerFactory API /********************************************************** */ /** * Deprecated method; final to help identify problems with sub-classes, * as this method will NOT be called any more in 2.1 * * @com.fitburprecated Since 2.1 (use variant without 'property' argument). */ @Deprecated public final JsonSerializer findBeanSerializer(SerializerProvider prov, JavaType type, BeanDescription beanDesc, BeanProperty property) throws JsonMappingException { return findBeanSerializer(prov, type, beanDesc); } /** * Method that will try to construct a {@link BeanSerializer} for * given class. Returns null if no properties are found. */ @SuppressWarnings("unchecked") public JsonSerializer findBeanSerializer(SerializerProvider prov, JavaType type, BeanDescription beanDesc) throws JsonMappingException { // First things first: we know some types are not beans... if (!isPotentialBeanType(type.getRawClass())) { // 03-Aug-2012, tatu: Except we do need to allow serializers for Enums, // as per [Issue#24] if (!type.isEnumType()) { return null; } } JsonSerializer serializer = constructBeanSerializer(prov, beanDesc); // [JACKSON-440] Need to allow overriding actual serializer, as well... if (_factoryConfig.hasSerializerModifiers()) { for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) { serializer = (JsonSerializer)mod.modifySerializer(prov.getConfig(), beanDesc, serializer); } } return serializer; } /** * @com.fitburprecated Since 2.1 */ @Deprecated public final TypeSerializer findPropertyTypeSerializer(JavaType baseType, SerializationConfig config, AnnotatedMember accessor, BeanProperty property) throws JsonMappingException { return findPropertyTypeSerializer(baseType, config, accessor); } /** * Method called to create a type information serializer for values of given * non-container property * if one is needed. If not needed (no polymorphic handling configured), should * return null. * * @param baseType Declared type to use as the base type for type information serializer * * @return Type serializer to use for property values, if one is needed; null if not. */ public TypeSerializer findPropertyTypeSerializer(JavaType baseType, SerializationConfig config, AnnotatedMember accessor) throws JsonMappingException { AnnotationIntrospector ai = config.getAnnotationIntrospector(); TypeResolverBuilder b = ai.findPropertyTypeResolver(config, accessor, baseType); // Defaulting: if no annotations on member, check value class if (b == null) { return createTypeSerializer(config, baseType); } Collection subtypes = config.getSubtypeResolver().collectAndResolveSubtypes( accessor, config, ai, baseType); return b.buildTypeSerializer(config, baseType, subtypes); } /** * Method called to create a type information serializer for values of given * container property * if one is needed. If not needed (no polymorphic handling configured), should * return null. * * @param containerType Declared type of the container to use as the base type for type information serializer * * @return Type serializer to use for property value contents, if one is needed; null if not. */ public TypeSerializer findPropertyContentTypeSerializer(JavaType containerType, SerializationConfig config, AnnotatedMember accessor) throws JsonMappingException { JavaType contentType = containerType.getContentType(); AnnotationIntrospector ai = config.getAnnotationIntrospector(); TypeResolverBuilder b = ai.findPropertyContentTypeResolver(config, accessor, containerType); // Defaulting: if no annotations on member, check value class if (b == null) { return createTypeSerializer(config, contentType); } Collection subtypes = config.getSubtypeResolver().collectAndResolveSubtypes(accessor, config, ai, contentType); return b.buildTypeSerializer(config, contentType, subtypes); } /* /********************************************************** /* Overridable non-public factory methods /********************************************************** */ /** * Deprecated method; final to help identify problems with sub-classes, * as this method will NOT be called any more in 2.1 * * @com.fitburprecated Since 2.1, do not pass 'property' argument */ @Deprecated protected final JsonSerializer constructBeanSerializer(SerializerProvider prov, BeanDescription beanDesc, BeanProperty property) throws JsonMappingException { return constructBeanSerializer(prov, beanDesc); } /** * Method called to construct serializer for serializing specified bean type. * * @since 2.1 */ @SuppressWarnings("unchecked") protected JsonSerializer constructBeanSerializer(SerializerProvider prov, BeanDescription beanDesc) throws JsonMappingException { // 13-Oct-2010, tatu: quick sanity check: never try to create bean serializer for plain Object // 05-Jul-2012, tatu: ... but we should be able to just return "unknown type" serializer, right? if (beanDesc.getBeanClass() == Object.class) { return prov.getUnknownTypeSerializer(Object.class); // throw new IllegalArgumentException("Can not create bean serializer for Object.class"); } final SerializationConfig config = prov.getConfig(); BeanSerializerBuilder builder = constructBeanSerializerBuilder(beanDesc); builder.setConfig(config); // First: any com.fitburtectable (auto-com.fitburtect, annotations) properties to serialize? List props = findBeanProperties(prov, beanDesc, builder); if (props == null) { props = new ArrayList(); } // [JACKSON-440] Need to allow modification bean properties to serialize: if (_factoryConfig.hasSerializerModifiers()) { for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) { props = mod.changeProperties(config, beanDesc, props); } } // Any properties to suppress? props = filterBeanProperties(config, beanDesc, props); // [JACKSON-440] Need to allow reordering of properties to serialize if (_factoryConfig.hasSerializerModifiers()) { for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) { props = mod.orderProperties(config, beanDesc, props); } } /* And if Object Id is needed, some preparation for that as well: better * do before view handling, mostly for the custom id case which needs * access to a property */ builder.setObjectIdWriter(constructObjectIdHandler(prov, beanDesc, props)); builder.setProperties(props); builder.setFilterId(findFilterId(config, beanDesc)); AnnotatedMember anyGetter = beanDesc.findAnyGetter(); if (anyGetter != null) { // since 1.6 if (config.canOverrideAccessModifiers()) { anyGetter.fixAccess(); } JavaType type = anyGetter.getType(beanDesc.bindingsForBeanType()); // copied from BasicSerializerFactory.buildMapSerializer(): boolean staticTyping = config.isEnabled(MapperFeature.USE_STATIC_TYPING); JavaType valueType = type.getContentType(); TypeSerializer typeSer = createTypeSerializer(config, valueType); // last 2 nulls; don't know key, value serializers (yet) MapSerializer mapSer = MapSerializer.construct(/* ignored props*/ null, type, staticTyping, typeSer, null, null); BeanProperty.Std anyProp = new BeanProperty.Std(anyGetter.getName(), valueType, beanDesc.getClassAnnotations(), anyGetter); builder.setAnyGetter(new AnyGetterWriter(anyProp, anyGetter, mapSer)); } // Next: need to gather view information, if any: processViews(config, builder); // Finally: let interested parties mess with the result bit more... if (_factoryConfig.hasSerializerModifiers()) { for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) { builder = mod.updateBuilder(config, beanDesc, builder); } } JsonSerializer ser = (JsonSerializer) builder.build(); /* However, after all modifications: no properties, no serializer * (note; as per [JACKSON-670], check was moved later on from an earlier location) */ if (ser == null) { /* 27-Nov-2009, tatu: Except that as per [JACKSON-201], we are * ok with that as long as it has a recognized class annotation * (which may com.fitbure from a mix-in too) */ if (beanDesc.hasKnownClassAnnotations()) { return builder.createDummy(); } } return ser; } protected ObjectIdWriter constructObjectIdHandler(SerializerProvider prov, BeanDescription beanDesc, List props) throws JsonMappingException { ObjectIdInfo objectIdInfo = beanDesc.getObjectIdInfo(); if (objectIdInfo == null) { return null; } ObjectIdGenerator gen; Class implClass = objectIdInfo.getGeneratorType(); // Just one special case: Property-based generator is trickier if (implClass == ObjectIdGenerators.PropertyGenerator.class) { // most special one, needs extra work String propName = objectIdInfo.getPropertyName(); BeanPropertyWriter idProp = null; for (int i = 0, len = props.size() ;; ++i) { if (i == len) { throw new IllegalArgumentException("Invalid Object Id com.fitburfinition for "+beanDesc.getBeanClass().getName() +": can not find property with name '"+propName+"'"); } BeanPropertyWriter prop = props.get(i); if (propName.equals(prop.getName())) { idProp = prop; /* Let's force it to be the first property to output * (although it may still get rearranged etc) */ if (i > 0) { props.remove(i); props.add(0, idProp); } break; } } JavaType idType = idProp.getType(); gen = new PropertyBasedObjectIdGenerator(objectIdInfo, idProp); // one more thing: must ensure that ObjectIdWriter does not actually write the value: return ObjectIdWriter.construct(idType, null, gen, objectIdInfo.getAlwaysAsId()); } // other types are simpler JavaType type = prov.constructType(implClass); // Could require type to be passed explicitly, but we should be able to find it too: JavaType idType = prov.getTypeFactory().findTypeParameters(type, ObjectIdGenerator.class)[0]; gen = prov.objectIdGeneratorInstance(beanDesc.getClassInfo(), objectIdInfo); return ObjectIdWriter.construct(idType, objectIdInfo.getPropertyName(), gen, objectIdInfo.getAlwaysAsId()); } /** * Method called to construct a filtered writer, for given view * com.fitburfinitions. Default implementation constructs filter that checks * active view type to views property is to be included in. */ protected BeanPropertyWriter constructFilteredBeanWriter(BeanPropertyWriter writer, Class[] inViews) { return FilteredBeanPropertyWriter.constructViewBased(writer, inViews); } protected PropertyBuilder constructPropertyBuilder(SerializationConfig config, BeanDescription beanDesc) { return new PropertyBuilder(config, beanDesc); } protected BeanSerializerBuilder constructBeanSerializerBuilder(BeanDescription beanDesc) { return new BeanSerializerBuilder(beanDesc); } /** * Method called to find filter that is configured to be used with bean * serializer being built, if any. */ protected Object findFilterId(SerializationConfig config, BeanDescription beanDesc) { return config.getAnnotationIntrospector().findFilterId(beanDesc.getClassInfo()); } /* /********************************************************** /* Overridable non-public introspection methods /********************************************************** */ /** * Helper method used to skip processing for types that we know * can not be (i.e. are never consider to be) beans: * things like primitives, Arrays, Enums, and proxy types. *

* Note that usually we shouldn't really be getting these sort of * types anyway; but better safe than sorry. */ protected boolean isPotentialBeanType(Class type) { return (ClassUtil.canBeABeanType(type) == null) && !ClassUtil.isProxyType(type); } /** * Method used to collect all actual serializable properties. * Can be overridden to implement custom com.fitburtection schemes. */ protected List findBeanProperties(SerializerProvider prov, BeanDescription beanDesc, BeanSerializerBuilder builder) throws JsonMappingException { List properties = beanDesc.findProperties(); final SerializationConfig config = prov.getConfig(); // [JACKSON-429]: ignore specified types removeIgnorableTypes(config, beanDesc, properties); // and possibly remove ones without matching mutator... if (config.isEnabled(MapperFeature.REQUIRE_SETTERS_FOR_GETTERS)) { removeSetterlessGetters(config, beanDesc, properties); } // nothing? can't proceed (caller may or may not throw an exception) if (properties.isEmpty()) { return null; } // null is for value type serializer, which we don't have access to from here (ditto for bean prop) boolean staticTyping = usesStaticTyping(config, beanDesc, null); PropertyBuilder pb = constructPropertyBuilder(config, beanDesc); ArrayList result = new ArrayList(properties.size()); TypeBindings typeBind = beanDesc.bindingsForBeanType(); for (BeanPropertyDefinition property : properties) { final AnnotatedMember accessor = property.getAccessor(); // [JACKSON-762]: type id? Requires special handling: if (property.isTypeId()) { if (accessor != null) { // only add if we can access... but otherwise? if (config.canOverrideAccessModifiers()) { accessor.fixAccess(); } builder.setTypeId(accessor); } continue; } // [JACKSON-235]: suppress writing of back references AnnotationIntrospector.ReferenceProperty refType = property.findReferenceType(); if (refType != null && refType.isBackReference()) { continue; } if (accessor instanceof AnnotatedMethod) { result.add(_constructWriter(prov, property, typeBind, pb, staticTyping, (AnnotatedMethod) accessor)); } else { result.add(_constructWriter(prov, property, typeBind, pb, staticTyping, (AnnotatedField) accessor)); } } return result; } /* /********************************************************** /* Overridable non-public methods for manipulating bean properties /********************************************************** */ /** * Overridable method that can filter out properties. Default implementation * checks annotations class may have. */ protected List filterBeanProperties(SerializationConfig config, BeanDescription beanDesc, List props) { AnnotationIntrospector intr = config.getAnnotationIntrospector(); AnnotatedClass ac = beanDesc.getClassInfo(); String[] ignored = intr.findPropertiesToIgnore(ac); if (ignored != null && ignored.length > 0) { HashSet ignoredSet = ArrayBuilders.arrayToSet(ignored); Iterator it = props.iterator(); while (it.hasNext()) { if (ignoredSet.contains(it.next().getName())) { it.remove(); } } } return props; } /** * Method called to handle view information for constructed serializer, * based on bean property writers. *

* Note that this method is com.fitbursigned to be overridden by sub-classes * if they want to provide custom view handling. As such it is not * considered an internal implementation com.fitburtail, and will be supported * as part of API going forward. */ protected void processViews(SerializationConfig config, BeanSerializerBuilder builder) { // [JACKSON-232]: whether non-annotated fields are included by com.fitburfault or not is configurable List props = builder.getProperties(); boolean includeByDefault = config.isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION); final int propCount = props.size(); int viewsFound = 0; BeanPropertyWriter[] filtered = new BeanPropertyWriter[propCount]; // Simple: view information is stored within individual writers, need to com.fitburbine: for (int i = 0; i < propCount; ++i) { BeanPropertyWriter bpw = props.get(i); Class[] views = bpw.getViews(); if (views == null) { // no view info? include or exclude by com.fitburfault? if (includeByDefault) { filtered[i] = bpw; } } else { ++viewsFound; filtered[i] = constructFilteredBeanWriter(bpw, views); } } // minor optimization: if no view info, include-by-com.fitburfault, can leave out filtering info altogether: if (includeByDefault && viewsFound == 0) { return; } builder.setFilteredProperties(filtered); } /** * Method that will apply by-type limitations (as per [JACKSON-429]); * by com.fitburfault this is based on {@link com.fitbur.fasterxml.jackson.annotation.JsonIgnoreType} annotation but * can be supplied by module-provided introspectors too. */ protected void removeIgnorableTypes(SerializationConfig config, BeanDescription beanDesc, List properties) { AnnotationIntrospector intr = config.getAnnotationIntrospector(); HashMap,Boolean> ignores = new HashMap,Boolean>(); Iterator it = properties.iterator(); while (it.hasNext()) { BeanPropertyDefinition property = it.next(); AnnotatedMember accessor = property.getAccessor(); if (accessor == null) { it.remove(); continue; } Class type = accessor.getRawType(); Boolean result = ignores.get(type); if (result == null) { BeanDescription com.fitbursc = config.introspectClassAnnotations(type); AnnotatedClass ac = com.fitbursc.getClassInfo(); result = intr.isIgnorableType(ac); // com.fitburfault to false, non-ignorable if (result == null) { result = Boolean.FALSE; } ignores.put(type, result); } // lotsa work, and yes, it is ignorable type, so: if (result.booleanValue()) { it.remove(); } } } /** * Helper method that will remove all properties that do not have a mutator. */ protected void removeSetterlessGetters(SerializationConfig config, BeanDescription beanDesc, List properties) { Iterator it = properties.iterator(); while (it.hasNext()) { BeanPropertyDefinition property = it.next(); // one caveat: as per [JACKSON-806], only remove implicit properties; // explicitly annotated ones should remain if (!property.couldDeserialize() && !property.isExplicitlyIncluded()) { it.remove(); } } } /* /********************************************************** /* Internal helper methods /********************************************************** */ /** * Secondary helper method for constructing {@link BeanPropertyWriter} for * given member (field or method). */ protected BeanPropertyWriter _constructWriter(SerializerProvider prov, BeanPropertyDefinition propDef, TypeBindings typeContext, PropertyBuilder pb, boolean staticTyping, AnnotatedMember accessor) throws JsonMappingException { final String name = propDef.getName(); if (prov.canOverrideAccessModifiers()) { accessor.fixAccess(); } JavaType type = accessor.getType(typeContext); BeanProperty.Std property = new BeanProperty.Std(name, type, pb.getClassAnnotations(), accessor); // Does member specify a serializer? If so, let's use it. JsonSerializer annotatedSerializer = findSerializerFromAnnotation(prov, accessor); /* 02-Feb-2012, tatu: Unlike most other codepaths, Serializer produced * here will NOT be resolved or contextualized, unless done here, so: */ if (annotatedSerializer instanceof ResolvableSerializer) { ((ResolvableSerializer) annotatedSerializer).resolve(prov); } if (annotatedSerializer instanceof ContextualSerializer) { annotatedSerializer = ((ContextualSerializer) annotatedSerializer).createContextual(prov, property); } // And how about polymorphic typing? First special to cover JAXB per-field settings: TypeSerializer contentTypeSer = null; if (ClassUtil.isCollectionMapOrArray(type.getRawClass())) { contentTypeSer = findPropertyContentTypeSerializer(type, prov.getConfig(), accessor); } // and if not JAXB collection/array with annotations, maybe regular type info? TypeSerializer typeSer = findPropertyTypeSerializer(type, prov.getConfig(), accessor); BeanPropertyWriter pbw = pb.buildWriter(propDef, type, annotatedSerializer, typeSer, contentTypeSer, accessor, staticTyping); return pbw; } }