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

com.fasterxml.jackson.xml.JacksonXmlModule Maven / Gradle / Ivy

package com.fasterxml.jackson.xml;

import java.util.*;

import javax.xml.namespace.QName;

import org.codehaus.jackson.Version;
import org.codehaus.jackson.map.AnnotationIntrospector;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.introspect.AnnotatedMember;
import org.codehaus.jackson.map.introspect.BasicBeanDescription;
import org.codehaus.jackson.map.module.SimpleModule;
import org.codehaus.jackson.map.ser.BeanPropertyWriter;
import org.codehaus.jackson.map.ser.BeanSerializer;
import org.codehaus.jackson.map.ser.BeanSerializerModifier;
import org.codehaus.jackson.type.JavaType;

import com.fasterxml.jackson.xml.ser.XmlBeanPropertyWriter;
import com.fasterxml.jackson.xml.ser.XmlBeanSerializer;
import com.fasterxml.jackson.xml.util.XmlInfo;

/**
 * Module that implements most functionality needed to support producing and
 * consuming XML instead of JSON.
 */
public class JacksonXmlModule extends SimpleModule
{
    private final static AnnotationIntrospector XML_ANNOTATION_INTROSPECTOR = new JacksonXmlAnnotationIntrospector();

    // !!! TODO: how to externalize version?
    private final static Version VERSION = new Version(0, 1, 0, null);
    
    public JacksonXmlModule()
    {
        super("JackxonXmlModule", VERSION);
    }
    
    @Override
    public void setupModule(SetupContext context)
    {
        context.addBeanSerializerModifier(new MySerializerModifier());
        context.insertAnnotationIntrospector(XML_ANNOTATION_INTROSPECTOR);
    }

    /*
    /**********************************************************
    /* Helper methods
    /**********************************************************
     */
    
    /**
     * Helper method used for figuring out if given raw type is a collection ("indexed") type;
     * in which case a wrapper element is typically added.
     */
    private static boolean _isContainerType(JavaType type)
    {
        if (type.isContainerType()) {
            // Just one special case; byte[] will be serialized as base64-encoded String, not real array, so:
            if (type.getRawClass() == byte[].class) {
                return false;
            }
            return true;
        }
        return false;
    }

    private static String findNamespaceAnnotation(AnnotationIntrospector ai, AnnotatedMember prop)
    {
        for (AnnotationIntrospector intr : ai.allIntrospectors()) {
            if (intr instanceof XmlAnnotationIntrospector) {
                String ns = ((XmlAnnotationIntrospector) intr).findNamespace(prop);
                if (ns != null) {
                    return ns;
                }
            }
        }
        return null;
    }

    private static Boolean findIsAttributeAnnotation(AnnotationIntrospector ai, AnnotatedMember prop)
    {
        for (AnnotationIntrospector intr : ai.allIntrospectors()) {
            if (intr instanceof XmlAnnotationIntrospector) {
                Boolean b = ((XmlAnnotationIntrospector) intr).isOutputAsAttribute(prop);
                if (b != null) {
                    return b;
                }
            }
        }
        return null;
    }

    private static QName findWrapperName(AnnotationIntrospector ai, AnnotatedMember prop)
    {
        for (AnnotationIntrospector intr : ai.allIntrospectors()) {
            if (intr instanceof XmlAnnotationIntrospector) {
                QName n = ((XmlAnnotationIntrospector) intr).findWrapperElement(prop);
                if (n != null) {
                    return n;
                }
            }
        }
        return null;
    }
    
    /*
    /**********************************************************
    /* Helper classes
    /**********************************************************
     */

    /**
     * We need a {@link SerializerModifier} to replace default BeanSerializer
     * with XML-specific one.
     */
    protected static class MySerializerModifier extends BeanSerializerModifier
    {
        /**
         * First thing to do is to find annotations regarding XML serialization,
         * and wrap collection serializers.
         */
        public List changeProperties(SerializationConfig config,
                BasicBeanDescription beanDesc, List beanProperties)
        {
            AnnotationIntrospector intr = config.getAnnotationIntrospector();
            for (int i = 0, len = beanProperties.size(); i < len; ++i) {
                BeanPropertyWriter bpw = beanProperties.get(i);
                final AnnotatedMember member = bpw.getMember();
                String ns = findNamespaceAnnotation(intr, member);
                Boolean isAttribute = findIsAttributeAnnotation(intr, member);
                bpw.setInternalSetting(XmlBeanSerializer.KEY_XML_INFO, new XmlInfo(isAttribute, ns));

                // Actually: if we have a Collection type, easiest place to add wrapping would be here...
                if (_isContainerType(bpw.getType())) {
                    String localName = null, wrapperNs = null;

                    QName wrappedName = new QName(ns, bpw.getName());
                    QName wrapperName = findWrapperName(intr, member);
                    if (wrapperName != null) {
                        localName = wrapperName.getLocalPart();
                        wrapperNs = wrapperName.getNamespaceURI();
                    }
                    /* Empty/missing localName means "use property name as wrapper"; later on
                     * should probably make missing (null) mean "don't add a wrapper"
                     */
                    if (localName == null || localName.length() == 0) {
                        wrapperName = wrappedName;
                    } else {
                        wrapperName = new QName((wrapperNs == null) ? "" : wrapperNs, localName);
                    }
                    beanProperties.set(i, new XmlBeanPropertyWriter(bpw, wrapperName, wrappedName));
                }
            }
            return beanProperties;
        }
        
        public JsonSerializer modifySerializer(SerializationConfig config,
                BasicBeanDescription beanDesc, JsonSerializer serializer)
        {
            /* First things first: we can only handle real BeanSerializers; question
             * is, what to do if it's not one: throw exception or bail out?
             * For now let's do latter.
             */
            if (!(serializer instanceof BeanSerializer)) {
                return serializer;
            }
            return new XmlBeanSerializer((BeanSerializer) serializer);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy