Package org.codehaus.jackson.map.ser

Source Code of org.codehaus.jackson.map.ser.BeanSerializerFactory

package org.codehaus.jackson.map.ser;

import java.util.*;

import java.lang.reflect.Method;

import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.SerializerFactory;
import org.codehaus.jackson.map.introspect.AnnotatedMethod;
import org.codehaus.jackson.map.introspect.BasicBeanDescription;
import org.codehaus.jackson.map.util.ClassUtil;

/**
* 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 since there is no configurability, this
* factory is stateless. And thus a global singleton instance can
* be used.
*/
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();

    /*
    ////////////////////////////////////////////////////////////
    // Life cycle
    ////////////////////////////////////////////////////////////
     */

    /**
     * We will provide default constructor to allow sub-classing,
     * but make it protected so that no non-singleton instances of
     * the class will be instantiated.
     */
    protected BeanSerializerFactory() { }

    /*
    ////////////////////////////////////////////////////////////
    // JsonSerializerFactory 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 <T> JsonSerializer<T> createSerializer(Class<T> type, SerializationConfig config)
    {
        // First, fast lookup for exact type:
        JsonSerializer<?> ser = super.findSerializerByLookup(type);
        if (ser == null) {
            // and then introspect for some safe (?) JDK types
            ser = super.findSerializerByPrimaryType(type, config);
            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 = this.findBeanSerializer(type, config);
                /* Finally: maybe we can still deal with it as an
                 * implementation of some basic JDK interface?
                 */
                if (ser == null) {
                    ser = super.findSerializerByAddonType(type);
                }
            }
        }
        return (JsonSerializer<T>) 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.
     */
    public JsonSerializer<Object> findBeanSerializer(Class<?> type, SerializationConfig config)
    {
        // First things first: we know some types are not beans...
        if (!isPotentialBeanType(type)) {
            return null;
        }
        BasicBeanDescription beanDesc = config.introspect(type);
        JsonSerializer<Object> ser = findSerializerFromAnnotation(beanDesc.getClassInfo());
        if (ser != null) {
            return ser;
        }

        /* [JACKSON-80]: Should support @JsonValue, which is alternative to
         *   actual bean method introspection.
         */
        AnnotatedMethod valueMethod = beanDesc.findJsonValue();
        if (valueMethod != null) {
            /* Further, method itself may also be annotated to indicate
             * exact JsonSerializer to use for whatever value is returned...
             */
            ser = findSerializerFromAnnotation(valueMethod);
            return new JsonValueSerializer(valueMethod.getAnnotated(), ser);
        }

        // First: what properties are to be serializable?
        Collection<BeanPropertyWriter> props = findBeanProperties(config, beanDesc);
        if (props == null || props.size() == 0) {
            // No properties, no serializer
            return null;
        }
        return new BeanSerializer(type, props);
    }

    /*
    ////////////////////////////////////////////////////////////
    // Overridable internal 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
     */
    protected Collection<BeanPropertyWriter> findBeanProperties(SerializationConfig config, BasicBeanDescription beanDesc)
    {
        // are getters auto-detected?
        boolean autodetect = config.isEnabled(SerializationConfig.Feature.AUTO_DETECT_GETTERS);
        LinkedHashMap<String,AnnotatedMethod> methodsByProp = beanDesc.findGetters(autodetect, null);
        // nothing? can't proceed
        if (methodsByProp.isEmpty()) {
            return null;
        }

        /* are null properties to be written for properties of
         * this class?
         */
        boolean writeNulls = beanDesc.willWriteNullProperties(config.isEnabled(SerializationConfig.Feature.WRITE_NULL_PROPERTIES));
        boolean fixAccess = config.isEnabled(SerializationConfig.Feature.CAN_OVERRIDE_ACCESS_MODIFIERS);

        ArrayList<BeanPropertyWriter> props = new ArrayList<BeanPropertyWriter>(methodsByProp.size());
        for (Map.Entry<String,AnnotatedMethod> en : methodsByProp.entrySet()) {
            AnnotatedMethod am = en.getValue();
            if (fixAccess) {
                am.fixAccess();
            }
            /* One more thing: does Method specify a serializer?
             * If so, let's use it.
             */
            JsonSerializer<Object> ser = findSerializerFromAnnotation(am);
            Method m = am.getAnnotated();
            // and finally, there may be per-method overrides:
            boolean methodNulls = am.willWriteNullProperties(writeNulls);
            props.add(new BeanPropertyWriter(en.getKey(), m, ser, methodNulls));
        }
        return props;
    }
}
TOP

Related Classes of org.codehaus.jackson.map.ser.BeanSerializerFactory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.