| package org.codehaus.jackson.map.ser; |
| |
| import java.util.*; |
| |
| import org.codehaus.jackson.map.*; |
| import org.codehaus.jackson.map.introspect.*; |
| import org.codehaus.jackson.map.jsontype.NamedType; |
| import org.codehaus.jackson.map.jsontype.TypeResolverBuilder; |
| import org.codehaus.jackson.map.ser.std.MapSerializer; |
| import org.codehaus.jackson.map.type.*; |
| import org.codehaus.jackson.map.util.ArrayBuilders; |
| import org.codehaus.jackson.map.util.ClassUtil; |
| import org.codehaus.jackson.type.JavaType; |
| |
| /** |
| * Factory class that can provide serializers for any regular Java beans |
| * (as defined 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 delegating 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. |
| *<p> |
| * Note about delegating calls to {@link BasicSerializerFactory}: |
| * although it would be nicer to use linear delegation |
| * 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 detecting 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" delegation used, and calls are NOT done using |
| * regular {@link SerializerFactory} interface but rather via |
| * direct calls to {@link BasicSerializerFactory}. |
| *<p> |
| * 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. |
| *<p> |
| * Notes for version 1.7 (and above): the new module registration system |
| * required addition of {@link #withConfig}, which has to |
| * be redefined by sub-classes so that they can work properly with |
| * pluggable additional serializer providing components. |
| */ |
| public class BeanSerializerFactory |
| extends BasicSerializerFactory |
| { |
| /** |
| * 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); |
| |
| /** |
| * Configuration settings for this factory; immutable instance (just like this |
| * factory), new version created via copy-constructor (fluent-style) |
| * |
| * @since 1.7 |
| */ |
| protected final Config _factoryConfig; |
| |
| /* |
| /********************************************************** |
| /* Config class implementation |
| /********************************************************** |
| */ |
| |
| /** |
| * Configuration settings container class for bean serializer factory |
| * |
| * @since 1.7 |
| */ |
| public static class ConfigImpl extends Config |
| { |
| /** |
| * Constant for empty <code>Serializers</code> array (which by definition |
| * is stateless and reusable) |
| */ |
| protected final static Serializers[] NO_SERIALIZERS = new Serializers[0]; |
| |
| protected final static BeanSerializerModifier[] NO_MODIFIERS = new BeanSerializerModifier[0]; |
| |
| /** |
| * List of providers for additional serializers, checked before considering default |
| * basic or bean serialializers. |
| * |
| * @since 1.7 |
| */ |
| protected final Serializers[] _additionalSerializers; |
| |
| /** |
| * @since 1.8 |
| */ |
| protected final Serializers[] _additionalKeySerializers; |
| |
| /** |
| * List of modifiers that can change the way {@link BeanSerializer} instances |
| * are configured and constructed. |
| */ |
| protected final BeanSerializerModifier[] _modifiers; |
| |
| public ConfigImpl() { |
| this(null, null, null); |
| } |
| |
| protected ConfigImpl(Serializers[] allAdditionalSerializers, |
| Serializers[] allAdditionalKeySerializers, |
| BeanSerializerModifier[] modifiers) |
| { |
| _additionalSerializers = (allAdditionalSerializers == null) ? |
| NO_SERIALIZERS : allAdditionalSerializers; |
| _additionalKeySerializers = (allAdditionalKeySerializers == null) ? |
| NO_SERIALIZERS : allAdditionalKeySerializers; |
| _modifiers = (modifiers == null) ? NO_MODIFIERS : modifiers; |
| } |
| |
| @Override |
| public Config withAdditionalSerializers(Serializers additional) |
| { |
| if (additional == null) { |
| throw new IllegalArgumentException("Can not pass null Serializers"); |
| } |
| Serializers[] all = ArrayBuilders.insertInListNoDup(_additionalSerializers, additional); |
| return new ConfigImpl(all, _additionalKeySerializers, _modifiers); |
| } |
| |
| @Override |
| public Config withAdditionalKeySerializers(Serializers additional) |
| { |
| if (additional == null) { |
| throw new IllegalArgumentException("Can not pass null Serializers"); |
| } |
| Serializers[] all = ArrayBuilders.insertInListNoDup(_additionalKeySerializers, additional); |
| return new ConfigImpl(_additionalSerializers, all, _modifiers); |
| } |
| |
| @Override |
| public Config withSerializerModifier(BeanSerializerModifier modifier) |
| { |
| if (modifier == null) { |
| throw new IllegalArgumentException("Can not pass null modifier"); |
| } |
| BeanSerializerModifier[] modifiers = ArrayBuilders.insertInListNoDup(_modifiers, modifier); |
| return new ConfigImpl(_additionalSerializers, _additionalKeySerializers, modifiers); |
| } |
| |
| @Override |
| public boolean hasSerializers() { return _additionalSerializers.length > 0; } |
| |
| @Override |
| public boolean hasKeySerializers() { return _additionalKeySerializers.length > 0; } |
| |
| @Override |
| public boolean hasSerializerModifiers() { return _modifiers.length > 0; } |
| |
| @Override |
| public Iterable<Serializers> serializers() { |
| return ArrayBuilders.arrayAsIterable(_additionalSerializers); |
| } |
| |
| @Override |
| public Iterable<Serializers> keySerializers() { |
| return ArrayBuilders.arrayAsIterable(_additionalKeySerializers); |
| } |
| |
| @Override |
| public Iterable<BeanSerializerModifier> serializerModifiers() { |
| return ArrayBuilders.arrayAsIterable(_modifiers); |
| } |
| } |
| |
| /* |
| /********************************************************** |
| /* Life-cycle: creation, configuration |
| /********************************************************** |
| */ |
| |
| /** |
| * Constructor for creating instances with specified configuration. |
| */ |
| protected BeanSerializerFactory(Config config) |
| { |
| if (config == null) { |
| config = new ConfigImpl(); |
| } |
| _factoryConfig = config; |
| } |
| |
| @Override public Config getConfig() { return _factoryConfig; } |
| |
| /** |
| * 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. |
| * |
| * @since 1.7 |
| */ |
| @Override |
| public SerializerFactory withConfig(Config 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 definitions"); |
| } |
| return new BeanSerializerFactory(config); |
| } |
| |
| @Override |
| protected Iterable<Serializers> 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}. |
| *<p> |
| * Note: sub-classes may choose to complete replace implementation, |
| * if they want to alter priority of serializer lookups. |
| */ |
| @Override |
| @SuppressWarnings("unchecked") |
| public JsonSerializer<Object> createSerializer(SerializationConfig config, JavaType origType, |
| BeanProperty property) |
| throws JsonMappingException |
| { |
| // Very first thing, let's check if there is explicit serializer annotation: |
| BasicBeanDescription beanDesc = config.introspect(origType); |
| JsonSerializer<?> ser = findSerializerFromAnnotation(config, beanDesc.getClassInfo(), property); |
| if (ser != null) { |
| return (JsonSerializer<Object>) ser; |
| } |
| |
| // Next: we may have annotations that further define types to use... |
| JavaType type = modifyTypeByAnnotation(config, beanDesc.getClassInfo(), origType); |
| // and if so, we consider it implicit "force static typing" instruction |
| boolean staticTyping = (type != origType); |
| if (type != origType && type.getRawClass() != origType.getRawClass()) { |
| // [JACKSON-799]: need to re-introspect |
| beanDesc = config.introspect(type); |
| } |
| |
| // Container types differ from non-container types: |
| if (origType.isContainerType()) { |
| return (JsonSerializer<Object>) buildContainerSerializer(config, type, beanDesc, property, staticTyping); |
| } |
| |
| // Modules may provide serializers of all types: |
| for (Serializers serializers : _factoryConfig.serializers()) { |
| ser = serializers.findSerializer(config, type, beanDesc, property); |
| if (ser != null) { |
| return (JsonSerializer<Object>) 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, property, staticTyping); |
| if (ser == null) { |
| ser = findSerializerByPrimaryType(type, config, beanDesc, property, staticTyping); |
| if (ser == null) { |
| /* And this is where this class comes 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(config, type, beanDesc, property); |
| /* Finally: maybe we can still deal with it as an |
| * implementation of some basic JDK interface? |
| */ |
| if (ser == null) { |
| ser = findSerializerByAddonType(config, type, beanDesc, property, staticTyping); |
| } |
| } |
| } |
| return (JsonSerializer<Object>) ser; |
| } |
| |
| @Override |
| @SuppressWarnings("unchecked") |
| public JsonSerializer<Object> createKeySerializer(SerializationConfig config, JavaType type, |
| BeanProperty property) |
| { |
| // Minor optimization: to avoid constructing beanDesc, bail out if none registered |
| if (!_factoryConfig.hasKeySerializers()) { |
| return null; |
| } |
| |
| // We should not need any member method info; at most class annotations for Map type |
| BasicBeanDescription beanDesc = config.introspectClassAnnotations(type.getRawClass()); |
| JsonSerializer<?> ser = null; |
| |
| // Only thing we have here are module-provided key serializers: |
| for (Serializers serializers : _factoryConfig.keySerializers()) { |
| ser = serializers.findSerializer(config, type, beanDesc, property); |
| if (ser != null) { |
| break; |
| } |
| } |
| return (JsonSerializer<Object>) ser; |
| } |
| |
| /* |
| /********************************************************** |
| /* Other public methods that are not part of |
| /* JsonSerializerFactory API |
| /********************************************************** |
| */ |
| |
| /** |
| * Method that will try to construct a {@link BeanSerializer} for |
| * given class. Returns null if no properties are found. |
| */ |
| @SuppressWarnings("unchecked") |
| public JsonSerializer<Object> findBeanSerializer(SerializationConfig config, JavaType type, |
| BasicBeanDescription beanDesc, BeanProperty property) |
| throws JsonMappingException |
| { |
| // First things first: we know some types are not beans... |
| if (!isPotentialBeanType(type.getRawClass())) { |
| return null; |
| } |
| JsonSerializer<Object> serializer = constructBeanSerializer(config, beanDesc, property); |
| // [JACKSON-440] Need to allow overriding actual serializer, as well... |
| if (_factoryConfig.hasSerializerModifiers()) { |
| for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) { |
| serializer = (JsonSerializer<Object>)mod.modifySerializer(config, beanDesc, serializer); |
| } |
| } |
| return serializer; |
| } |
| |
| /** |
| * 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. |
| * |
| * @since 1.5 |
| */ |
| public TypeSerializer findPropertyTypeSerializer(JavaType baseType, SerializationConfig config, |
| AnnotatedMember accessor, BeanProperty property) |
| 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, property); |
| } |
| Collection<NamedType> subtypes = config.getSubtypeResolver().collectAndResolveSubtypes(accessor, config, ai); |
| return b.buildTypeSerializer(config, baseType, subtypes, property); |
| } |
| |
| /** |
| * 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. |
| * |
| * @since 1.5 |
| */ |
| public TypeSerializer findPropertyContentTypeSerializer(JavaType containerType, SerializationConfig config, |
| AnnotatedMember accessor, BeanProperty property) |
| 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, property); |
| } |
| Collection<NamedType> subtypes = config.getSubtypeResolver().collectAndResolveSubtypes(accessor, config, ai); |
| return b.buildTypeSerializer(config, contentType, subtypes, property); |
| } |
| |
| /* |
| /********************************************************** |
| /* Overridable non-public factory methods |
| /********************************************************** |
| */ |
| |
| /** |
| * Method called to construct serializer for serializing specified bean type. |
| * |
| * @since 1.6 |
| */ |
| @SuppressWarnings("unchecked") |
| protected JsonSerializer<Object> constructBeanSerializer(SerializationConfig config, |
| BasicBeanDescription beanDesc, BeanProperty property) |
| throws JsonMappingException |
| { |
| // 13-Oct-2010, tatu: quick sanity check: never try to create bean serializer for plain Object |
| if (beanDesc.getBeanClass() == Object.class) { |
| throw new IllegalArgumentException("Can not create bean serializer for Object.class"); |
| } |
| |
| BeanSerializerBuilder builder = constructBeanSerializerBuilder(beanDesc); |
| |
| // First: any detectable (auto-detect, annotations) properties to serialize? |
| List<BeanPropertyWriter> props = findBeanProperties(config, beanDesc); |
| |
| if (props == null) { |
| props = new ArrayList<BeanPropertyWriter>(); |
| } |
| // [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); |
| // Do they need to be sorted in some special way? |
| props = sortBeanProperties(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); |
| } |
| } |
| |
| builder.setProperties(props); |
| builder.setFilterId(findFilterId(config, beanDesc)); |
| |
| AnnotatedMethod anyGetter = beanDesc.findAnyGetter(); |
| if (anyGetter != null) { // since 1.6 |
| if (config.isEnabled(SerializationConfig.Feature.CAN_OVERRIDE_ACCESS_MODIFIERS)) { |
| anyGetter.fixAccess(); |
| } |
| JavaType type = anyGetter.getType(beanDesc.bindingsForBeanType()); |
| // copied from BasicSerializerFactory.buildMapSerializer(): |
| boolean staticTyping = config.isEnabled(SerializationConfig.Feature.USE_STATIC_TYPING); |
| JavaType valueType = type.getContentType(); |
| TypeSerializer typeSer = createTypeSerializer(config, valueType, property); |
| // last 2 nulls; don't know key, value serializers (yet) |
| MapSerializer mapSer = MapSerializer.construct(/* ignored props*/ null, type, staticTyping, |
| typeSer, property, null, null); |
| builder.setAnyGetter(new AnyGetterWriter(anyGetter, mapSer)); |
| } |
| // One more thing: need to gather view information, if any: |
| processViews(config, builder); |
| // And maybe let interested parties mess with the result bit more... |
| if (_factoryConfig.hasSerializerModifiers()) { |
| for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) { |
| builder = mod.updateBuilder(config, beanDesc, builder); |
| } |
| } |
| JsonSerializer<Object> ser = (JsonSerializer<Object>) 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 come from a mix-in too) |
| */ |
| if (beanDesc.hasKnownClassAnnotations()) { |
| return builder.createDummy(); |
| } |
| } |
| return ser; |
| } |
| |
| /** |
| * Method called to construct a filtered writer, for given view |
| * definitions. 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, |
| BasicBeanDescription beanDesc) |
| { |
| return new PropertyBuilder(config, beanDesc); |
| } |
| |
| protected BeanSerializerBuilder constructBeanSerializerBuilder(BasicBeanDescription beanDesc) { |
| return new BeanSerializerBuilder(beanDesc); |
| } |
| |
| /** |
| * Method called to find filter that is configured to be used with bean |
| * serializer being built, if any. |
| * |
| * @since 1.7 |
| */ |
| protected Object findFilterId(SerializationConfig config, BasicBeanDescription 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. |
| *<p> |
| * 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 detection schemes. |
| */ |
| protected List<BeanPropertyWriter> findBeanProperties(SerializationConfig config, BasicBeanDescription beanDesc) |
| throws JsonMappingException |
| { |
| List<BeanPropertyDefinition> properties = beanDesc.findProperties(); |
| AnnotationIntrospector intr = config.getAnnotationIntrospector(); |
| |
| // [JACKSON-429]: ignore specified types |
| removeIgnorableTypes(config, beanDesc, properties); |
| |
| // and possibly remove ones without matching mutator... |
| if (config.isEnabled(SerializationConfig.Feature.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, null); |
| PropertyBuilder pb = constructPropertyBuilder(config, beanDesc); |
| |
| ArrayList<BeanPropertyWriter> result = new ArrayList<BeanPropertyWriter>(properties.size()); |
| TypeBindings typeBind = beanDesc.bindingsForBeanType(); |
| // [JACKSON-98]: start with field properties, if any |
| for (BeanPropertyDefinition property : properties) { |
| AnnotatedMember accessor = property.getAccessor(); |
| // [JACKSON-235]: suppress writing of back references |
| AnnotationIntrospector.ReferenceProperty prop = intr.findReferenceType(accessor); |
| if (prop != null && prop.isBackReference()) { |
| continue; |
| } |
| String name = property.getName(); |
| if (accessor instanceof AnnotatedMethod) { |
| result.add(_constructWriter(config, typeBind, pb, staticTyping, name, (AnnotatedMethod) accessor)); |
| } else { |
| result.add(_constructWriter(config, typeBind, pb, staticTyping, name, (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<BeanPropertyWriter> filterBeanProperties(SerializationConfig config, |
| BasicBeanDescription beanDesc, List<BeanPropertyWriter> props) |
| { |
| AnnotationIntrospector intr = config.getAnnotationIntrospector(); |
| AnnotatedClass ac = beanDesc.getClassInfo(); |
| String[] ignored = intr.findPropertiesToIgnore(ac); |
| if (ignored != null && ignored.length > 0) { |
| HashSet<String> ignoredSet = ArrayBuilders.arrayToSet(ignored); |
| Iterator<BeanPropertyWriter> it = props.iterator(); |
| while (it.hasNext()) { |
| if (ignoredSet.contains(it.next().getName())) { |
| it.remove(); |
| } |
| } |
| } |
| return props; |
| } |
| |
| /** |
| * Overridable method that will impose given partial ordering on |
| * list of discovered propertied. Method can be overridden to |
| * provide custom ordering of properties, beyond configurability |
| * offered by annotations (whic allow alphabetic ordering, as |
| * well as explicit ordering by providing array of property names). |
| *<p> |
| * By default Creator properties will be ordered before other |
| * properties. Explicit custom ordering will override this implicit |
| * default ordering. |
| */ |
| /** |
| * Method that used to be called (pre-1.9) to impose configured |
| * ordering on list of discovered properties. |
| * With 1.9 it is not needed any more as ordering is done earlier. |
| * |
| * @deprecated Since 1.9 this method does nothing, so there is no |
| * benefit from overriding it; it will be removed from 2.0. |
| */ |
| @Deprecated |
| protected List<BeanPropertyWriter> sortBeanProperties(SerializationConfig config, |
| BasicBeanDescription beanDesc, List<BeanPropertyWriter> props) |
| { |
| return props; |
| } |
| |
| /** |
| * Method called to handle view information for constructed serializer, |
| * based on bean property writers. |
| *<p> |
| * Note that this method is designed to be overridden by sub-classes |
| * if they want to provide custom view handling. As such it is not |
| * considered an internal implementation detail, and will be supported |
| * as part of API going forward. |
| *<p> |
| * NOTE: signature of this method changed in 1.7, due to other significant |
| * changes (esp. use of builder for serializer construction). |
| */ |
| protected void processViews(SerializationConfig config, BeanSerializerBuilder builder) |
| { |
| // [JACKSON-232]: whether non-annotated fields are included by default or not is configurable |
| List<BeanPropertyWriter> props = builder.getProperties(); |
| boolean includeByDefault = config.isEnabled(SerializationConfig.Feature.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 combine: |
| 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 default? |
| if (includeByDefault) { |
| filtered[i] = bpw; |
| } |
| } else { |
| ++viewsFound; |
| filtered[i] = constructFilteredBeanWriter(bpw, views); |
| } |
| } |
| // minor optimization: if no view info, include-by-default, 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 default this is based on {@link org.codehaus.jackson.annotate.JsonIgnoreType} annotation but |
| * can be supplied by module-provided introspectors too. |
| */ |
| protected void removeIgnorableTypes(SerializationConfig config, BasicBeanDescription beanDesc, |
| List<BeanPropertyDefinition> properties) |
| { |
| AnnotationIntrospector intr = config.getAnnotationIntrospector(); |
| HashMap<Class<?>,Boolean> ignores = new HashMap<Class<?>,Boolean>(); |
| Iterator<BeanPropertyDefinition> 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) { |
| BasicBeanDescription desc = config.introspectClassAnnotations(type); |
| AnnotatedClass ac = desc.getClassInfo(); |
| result = intr.isIgnorableType(ac); |
| // default 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. |
| * |
| * @since 1.9 |
| */ |
| protected void removeSetterlessGetters(SerializationConfig config, BasicBeanDescription beanDesc, |
| List<BeanPropertyDefinition> properties) |
| { |
| Iterator<BeanPropertyDefinition> 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(SerializationConfig config, TypeBindings typeContext, |
| PropertyBuilder pb, boolean staticTyping, String name, AnnotatedMember accessor) |
| throws JsonMappingException |
| { |
| if (config.isEnabled(SerializationConfig.Feature.CAN_OVERRIDE_ACCESS_MODIFIERS)) { |
| 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<Object> annotatedSerializer = findSerializerFromAnnotation(config, accessor, 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, config, accessor, property); |
| } |
| |
| // and if not JAXB collection/array with annotations, maybe regular type info? |
| TypeSerializer typeSer = findPropertyTypeSerializer(type, config, accessor, property); |
| BeanPropertyWriter pbw = pb.buildWriter(name, type, annotatedSerializer, |
| typeSer, contentTypeSer, accessor, staticTyping); |
| // how about views? (1.4+) |
| AnnotationIntrospector intr = config.getAnnotationIntrospector(); |
| pbw.setViews(intr.findSerializationViews(accessor)); |
| return pbw; |
| } |
| } |