Bootstrap

jackson:基于BeanSerializer实现自定义的Java bean序列化器

之前写过一篇博客《jackson:基于BeanDeserializer实现自定义的Java bean 解析器》,介绍了如何继承com.fasterxml.jackson.databind.deser.BeanDeserializer实现自定义的反序列化器。

如果要实现继承BeanDeserializer实现自定义的Java bean 解析器,与做序列化器一样,如何将一个Class转为BeanSserializer构造方法需要的类型才是关键。

BeanSerializerBuilder

如下是BeanSserializer的构造方法:

public BeanSerializer(JavaType type, BeanSerializerBuilder builder,
            BeanPropertyWriter[] properties, BeanPropertyWriter[] filteredProperties);
protected BeanSerializer(BeanSerializerBase src);
protected BeanSerializer(BeanSerializerBase src,ObjectIdWriter objectIdWriter);
protected BeanSerializer(BeanSerializerBase src,ObjectIdWriter objectIdWriter,Object filterId);
protected BeanSerializer(BeanSerializerBase src, Set<String> toIgnore, Set<String> toInclude);
protected BeanSerializer(BeanSerializerBase src,BeanPropertyWriter[] properties, BeanPropertyWriter[] filteredProperties)

这么多构造方法,从哪一个开始是个问题,通过反复跟踪代码,在BeanSerializeFactory.constructBeanOrAddOnSerializer(SerializerProvider prov, JavaType type, BeanDescription beanDesc, boolean staticTyping) 方法中找到了从Class创建BeanSerializerBuilder实例的代码。

上面BeanSserializer的第一个构造方法虽然要的参数多,但是有了BeanSerializerBuilder实例,所有的其他参数都可以想办法从BeanSerializerBuilder实例中获取 。

JacksonBeanSerializerFactory

由此我继承BeanSerializeFactory创建了一个新的类JacksonBeanSerializerFactory,只为照抄父类方法constructBeanOrAddOnSerializer的逻辑,
下面代码中JacksonBeanSerializerFactory.constructBeanSerializerBuilder方法只为从BeanDescription实例返回一个BeanSerializerBuilder实例

JacksonBeanSerializerFactory.java

class JacksonBeanSerializerFactory extends BeanSerializerFactory {

	private static final long serialVersionUID = 4346918816045771010L;
	static JacksonBeanSerializerFactory instance = new JacksonBeanSerializerFactory();
	public JacksonBeanSerializerFactory() {
		this(null);
	}
	
	public JacksonBeanSerializerFactory(SerializerFactoryConfig config) {
		super(config);
	}

	
    JacksonBeanSerializerBuilder constructBeanSerializerBuilder(SerializerProvider prov,
            BeanDescription beanDesc)
        throws JsonMappingException
    {
        final SerializationConfig config = prov.getConfig();
        JacksonBeanSerializerBuilder builder = new JacksonBeanSerializerBuilder(beanDesc);
        builder.setConfig(config);

        // First: any detectable (auto-detect, annotations) properties to serialize?
        List<BeanPropertyWriter> props = findBeanProperties(prov, beanDesc, builder);
        if (props == null) {
            props = new ArrayList<BeanPropertyWriter>();
        } else {
            props = removeOverlappingTypeIds(prov, beanDesc, builder, props);
        }
        
        // [databind#638]: Allow injection of "virtual" properties:
        prov.getAnnotationIntrospector().findAndAddVirtualProperties(config, beanDesc.getClassInfo(), props);

        // [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);

        // 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) {
            JavaType type = anyGetter.getType();
            // 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)
            // 23-Feb-2015, tatu: As per [databind#705], need to support custom serializers
            JsonSerializer<?> anySer = findSerializerFromAnnotation(prov, anyGetter);
            if (anySer == null) {
                // TODO: support '@JsonIgnoreProperties' with any setter?
                anySer = MapSerializer.construct(/* ignored props*/ (Set<String>) null,
                        type, staticTyping, typeSer, null, null, /*filterId*/ null);
            }
            // TODO: can we find full PropertyName?
            PropertyName name = PropertyName.construct(anyGetter.getName());
            BeanProperty.Std anyProp = new BeanProperty.Std(name, valueType, null,
                    beanDesc.getClassAnnotations(), anyGetter, PropertyMetadata.STD_OPTIONAL);
            builder.setAnyGetter(new AnyGetterWriter(anyProp, anyGetter, anySer));
        }
        // 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);
//            }
//        }

        return builder.builder();

    }

}

createBeanSerializer

然后我们就可以实现一个方法从Class创建一个BeanSerializerBuilder的实例:如下

	/**
	 * 创建{@code beanClass}对应的{@link BeanDeserializerBase}实例用于父类构造方法的参数,
	 * 将{@code beanClass}的序列化参数注入到当前实例中
	 * @param beanClass
	 */
	private static JacksonBeanSerializerBuilder createBeanSerializer(Class<?> beanClass){
		try {
			JavaType type = TypeFactory.defaultInstance().constructType(beanClass);
			ObjectMapper mapper = new ObjectMapper();
			BeanDescription desc = mapper.getSerializationConfig().introspect(type);
			SerializerProvider provider = ((DefaultSerializerProvider) mapper.getSerializerProvider())
							.createInstance(mapper.getSerializationConfig(), JacksonBeanSerializerFactory.instance);
			return JacksonBeanSerializerFactory.instance
					.constructBeanSerializerBuilder(provider, desc);
		} catch (Exception e) {
			throw new ExceptionInInitializerError(e);
		}
	}

JacksonBeanSerializerBuilder

上面方法中返回类型JacksonBeanSerializerBuilderBeanSerializerBuilder的子类,
实现代码如下:
JacksonBeanSerializerBuilder.java

import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerBuilder;

/**
 * @author guyadong
 * @since 3.31.3
 */
class JacksonBeanSerializerBuilder extends BeanSerializerBuilder {
	private final static BeanPropertyWriter[] NO_PROPERTIES = new BeanPropertyWriter[0];
	JacksonBeanSerializerBuilder(BeanDescription beanDesc) {
		super(beanDesc);
	}

	JacksonBeanSerializerBuilder(BeanSerializerBuilder src) {
		super(src);
	}
    public void setConfig(SerializationConfig config) {
        _config = config;
    }
    JacksonBeanSerializerBuilder builder()
    {
        if (_anyGetter != null) {
            _anyGetter.fixAccess(_config);
        }
        if (_typeId != null) {
            if (_config.isEnabled(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS)) {
                _typeId.fixAccess(_config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
            }
        }
        return this;
    }
    /**
     * @see #build()
     */
    BeanPropertyWriter[] buildProperties() {
        BeanPropertyWriter[] properties;
        // No properties, any getter or object id writer?
        // No real serializer; caller gets to handle
        if (_properties == null || _properties.isEmpty()) {
            if (_anyGetter == null && _objectIdWriter == null) {
                return null;
            }
            properties = NO_PROPERTIES;
        } else {
            properties = _properties.toArray(new BeanPropertyWriter[_properties.size()]);
            if (_config.isEnabled(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS)) {
                for (int i = 0, end = properties.length; i < end; ++i) {
                    properties[i].fixAccess(_config);
                }
            }
        }
        return properties;
    }
}

示例:JacksonExampleSerializer

有了createBeanSerializer方法,我们就可以很容易继承BeanSerializer实现自定义的序列化器了,示例如下:

JacksonExampleSerializer.java

import java.io.IOException;

import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.BeanSerializer;
import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider;
import com.fasterxml.jackson.databind.type.TypeFactory;

@SuppressWarnings("serial")
public abstract class JacksonExampleSerializer extends BeanSerializer {
	public JacksonExampleSerializer(Class<?> beanClass) {
		this(	createBeanSerializer(beanClass));
	}
	private JacksonExampleSerializer(JacksonBeanSerializerBuilder builder) {
		super(builder.getBeanDescription().getType(),
				builder,builder.buildProperties(),builder.getFilteredProperties());
	}
	
	@Override
	protected void serializeFieldsFiltered(Object bean, JsonGenerator gen, SerializerProvider provider)
			throws IOException, JsonGenerationException {
		// 根据需要重写实现方法
		super.serializeFieldsFiltered(bean, gen, provider);
	}
	/**
	 * 创建{@code beanClass}对应的{@link BeanSerializerBuilder}实例用于父类构造方法的参数,
	 * 将{@code beanClass}的序列化参数注入到当前实例中
	 * @param beanClass
	 */
	private static JacksonBeanSerializerBuilder createBeanSerializer(Class<?> beanClass){
		try {
			JavaType type = TypeFactory.defaultInstance().constructType(beanClass);
			ObjectMapper mapper = new ObjectMapper();
			BeanDescription desc = mapper.getSerializationConfig().introspect(type);
			SerializerProvider provider = ((DefaultSerializerProvider) mapper.getSerializerProvider())
							.createInstance(mapper.getSerializationConfig(), JacksonBeanSerializerFactory.instance);
			return JacksonBeanSerializerFactory.instance
					.constructBeanSerializerBuilder(provider, desc);
		} catch (Exception e) {
			throw new ExceptionInInitializerError(e);
		}
	}
}

;