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

org.codehaus.jackson.map.ser.CustomSerializerFactory Maven / Gradle / Ivy

Go to download

Data Mapper package is a high-performance data binding package built on Jackson JSON processor

There is a newer version: 1.9.13
Show newest version
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;

/**
 * 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) *
  • *
*

* In near future, following features are planned to be added: *

    *
  • Ability to define "mix-in annotations": associations between types * (classes, interfaces) to serialize, and a "mix-in" type which will * be used so that all of its annotations are added to the serialized * type. Mixed-in annotations have priority over annotations that the * serialized type has. In effect this allows for overriding annotations * types have; this is useful when type definition itself can not be * modified *
  • *
*/ 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. */ 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". */ JsonSerializer _enumSerializerOverride; /* //////////////////////////////////////////////////// // Configuration, generic (interface, super-class) mappings //////////////////////////////////////////////////// */ /** * And then class-based mappings that are used both for exact and * sub-class matches. */ HashMap> _transitiveClassMappings = null; /** * And finally interface-based matches. */ HashMap> _interfaceMappings = null; /* ////////////////////////////////////////////////////////// // Configuration: "mix-in annotation" mappings ////////////////////////////////////////////////////////// */ /** * Mapping that defines how to apply mix-in annotations: key is * the type to received additional annotations, and value is the * type that has annotations to "mix in". *

* !!! 30-Mar-2009, tatu: Not used as of yet */ HashMap> _mixInAnnotations; /* //////////////////////////////////////////////////// // Life-cycle, constructors //////////////////////////////////////////////////// */ public CustomSerializerFactory() { super(); } /* //////////////////////////////////////////////////// // 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 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 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; } /** * Method to use for adding mix-in annotations that Class * classWithMixIns contains into class * destinationClass. Mixing in is done when introspecting * class annotations and properties. * Annotations from classWithMixIns (and its supertypes) * will override * anything destinationClass (and its super-types) * has already. * * @param destinationClass Type to modify by adding annotations * @param classWithMixIns Type that contains annotations to add */ public void addMixInAnnotationMapping(Class destinationClass, Class classWithMixIns) { if (_mixInAnnotations == null) { _mixInAnnotations = new HashMap>(); } _mixInAnnotations.put(new ClassKey(destinationClass), classWithMixIns); } /* //////////////////////////////////////////////////// // JsonSerializerFactory impl //////////////////////////////////////////////////// */ @Override @SuppressWarnings("unchecked") public JsonSerializer createSerializer(Class type, SerializationConfig config) { JsonSerializer ser = null; ClassKey key = new ClassKey(type); // First: exact matches if (_directClassMappings != null) { ser = _directClassMappings.get(key); if (ser != null) { return (JsonSerializer) ser; } } // No match? Perhaps we can use the enum serializer? if (type.isEnum()) { if (_enumSerializerOverride != null) { return (JsonSerializer) _enumSerializerOverride; } } // Still no match? How about more generic ones? // Mappings for super-classes? if (_transitiveClassMappings != null) { for (Class curr = type; (curr != null); curr = curr.getSuperclass()) { key.reset(curr); ser = _transitiveClassMappings.get(key); if (ser != null) { return (JsonSerializer) ser; } } } // And if still no match, how about interfaces? if (_interfaceMappings != null) { for (Class curr = type; (curr != null); curr = curr.getSuperclass()) { for (Class iface : curr.getInterfaces()) { key.reset(iface); ser = _interfaceMappings.get(key); if (ser != null) { return (JsonSerializer) ser; } } } } /* And barring any other complications, let's just let * bean (or basic) serializer factory handle construction. */ return super.createSerializer(type, config); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy