org.codehaus.jackson.map.ser.CustomSerializerFactory Maven / Gradle / Ivy
Show all versions of jackson-mapper-asl Show documentation
package org.codehaus.jackson.map.ser;
import java.lang.reflect.Modifier;
import java.util.*;
import org.codehaus.jackson.map.*;
import org.codehaus.jackson.map.type.ClassKey;
import org.codehaus.jackson.type.JavaType;
/**
* Serializer factory implementation that allows for configuring
* mapping between types (classes) and serializers to use, by using
* multiple types of overrides. Existing mappings established by
* {@link BeanSerializerFactory} (and its super class,
* {@link BasicSerializerFactory}) are used if no overrides are
* defined.
*
* Unlike base serializer factories ({@link BasicSerializerFactory},
* {@link BeanSerializerFactory}), this factory is stateful because
* of configuration settings. It is thread-safe, however, as long as
* all configuration as done before using the factory -- a single
* instance can be shared between providers and mappers.
*
* Configurations currently available are:
*
* - Ability to define explicit mappings between classes and interfaces
* and serializers to use. These can be either specific ones (class must
* match exactly) or generic ones (any sub-class or class implementing
* the interface); specific ones have precedence over generic ones (and
* precedence between generic ones is not defined).
*
* - Ability to define a single generic base serializer for all Enum
* types (precedence below specific serializer mapping)
*
*
*
* Note: as of version 1.7, this class is not as useful as it used to
* be, due to addition of "modules", which allow simpler addition
* of custom serializers and deserializers. In fact, use of module system
* is recommended even when not exposing serializers or deserializers
* as a pluggable library (just using them locally).
*/
public class CustomSerializerFactory
extends BeanSerializerFactory
{
/*
/**********************************************************
/* Configuration, direct/special mappings
/**********************************************************
*/
/**
* Direct mappings that are only used for exact class type
* matches, but not for sub-class checks.
*/
protected HashMap> _directClassMappings = null;
/**
* And for Enum handling we may specify a single default
* serializer to use, regardless of actual enumeration.
* Usually used to provide "toString - serializer".
*/
protected JsonSerializer> _enumSerializerOverride;
/*
/**********************************************************
/* Configuration, generic (interface, super-class) mappings
/**********************************************************
*/
/**
* And then class-based mappings that are used both for exact and
* sub-class matches.
*/
protected HashMap> _transitiveClassMappings = null;
/**
* And finally interface-based matches.
*/
protected HashMap> _interfaceMappings = null;
/*
/**********************************************************
/* Life-cycle, constructors
/**********************************************************
*/
public CustomSerializerFactory() {
this(null);
}
public CustomSerializerFactory(Config config) {
super(config);
}
@Override
public SerializerFactory withConfig(Config config)
{
/* 22-Nov-2010, tatu: As with BeanSerializerFactory, must ensure type won't change
* with this method, so:
*/
if (getClass() != CustomSerializerFactory.class) {
throw new IllegalStateException("Subtype of CustomSerializerFactory ("+getClass().getName()
+") has not properly overridden method 'withAdditionalSerializers': can not instantiate subtype with "
+"additional serializer definitions");
}
return new CustomSerializerFactory(config);
}
/*
/**********************************************************
/* Configuration: type-to-serializer mappings
/**********************************************************
*/
/**
* Method used to add a generic (transitive) mapping from specified
* class or its sub-classes into a serializer.
* When resolving a type into a serializer, explicit class is checked
* first, then immediate super-class, and so forth along inheritance
* chain. But if this fails, implemented interfaces are checked;
* ordering is done such that first interfaces implemented by
* the exact type are checked (in order returned by
* {@link Class#getInterfaces}), then super-type's and so forth.
*
* Note that adding generic mappings may lead to problems with
* sub-classing: if sub-classes add new properties, these may not
* get properly serialized.
*
* @param type Class for which specified serializer is to be
* used. May be more specific type than what serializer indicates,
* but must be compatible (same or sub-class)
*/
public void addGenericMapping(Class extends T> type, JsonSerializer ser)
{
// Interface to match?
ClassKey key = new ClassKey(type);
if (type.isInterface()) {
if (_interfaceMappings == null) {
_interfaceMappings = new HashMap>();
}
_interfaceMappings.put(key, ser);
} else { // nope, class:
if (_transitiveClassMappings == null) {
_transitiveClassMappings = new HashMap>();
}
_transitiveClassMappings.put(key, ser);
}
}
/**
* Method used to add a mapping from specific type -- and only that
* type -- to specified serializer. This means that binding is not
* used for sub-types. It also means that no such mappings are to
* be defined for abstract classes or interfaces: and if an attempt
* is made, {@link IllegalArgumentException} will be thrown to
* indicate caller error.
*
* @param forClass Class for which specified serializer is to be
* used. May be more specific type than what serializer indicates,
* but must be compatible (same or sub-class)
*/
public void addSpecificMapping(Class extends T> forClass, JsonSerializer ser)
{
ClassKey key = new ClassKey(forClass);
/* First, let's ensure it's not an interface or abstract class:
* as those can not be instantiated, such mappings would never
* get used.
*/
if (forClass.isInterface()) {
throw new IllegalArgumentException("Can not add specific mapping for an interface ("+forClass.getName()+")");
}
if (Modifier.isAbstract(forClass.getModifiers())) {
throw new IllegalArgumentException("Can not add specific mapping for an abstract class ("+forClass.getName()+")");
}
if (_directClassMappings == null) {
_directClassMappings = new HashMap>();
}
_directClassMappings.put(key, ser);
}
/**
* Method that can be used to force specified serializer to be used for
* serializing all Enum instances. This is most commonly used to specify
* serializers that call either enum.toString()
, or modify
* value returned by enum.name()
(such as upper- or
* lower-casing it).
*
* Note: this serializer has lower precedence than that of specific
* types; so if a specific serializer is assigned to an Enum type,
* this serializer will NOT be used. It has higher precedence than
* generic mappings have however.
*/
public void setEnumSerializer(JsonSerializer> enumSer)
{
_enumSerializerOverride = enumSer;
}
/*
/**********************************************************
/* JsonSerializerFactory impl
/**********************************************************
*/
@Override
@SuppressWarnings("unchecked")
public JsonSerializer