nl.colorize.util.xml.XMLConverter.java Source code

Java tutorial

Introduction

Here is the source code for nl.colorize.util.xml.XMLConverter.java

Source

//-----------------------------------------------------------------------------
// Colorize Java Commons
// Copyright 2007-2019 Colorize
// Apache license (http://www.colorize.nl/code_license.txt)
//-----------------------------------------------------------------------------

package nl.colorize.util.xml;

import java.lang.reflect.Array;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import com.google.gson.internal.Primitives;

import org.jdom2.Document;
import org.jdom2.Element;

import nl.colorize.util.ReflectionUtils;

/**
 * Converts Java object structures into XML elements. This takes the approach
 * that the Gson library (https://code.google.com/p/google-gson/) uses for JSON
 * and applies it to XML. Like Gson, conversion does not require annotations.
 * This fixes two problematic aspects of the JAXB framework: it prevents XML-related
 * annotations from being added to non-XML-related classes, and allows you to also
 * convert classes for which you do not have the source code.
 * <p>
 * By default, all non-primitive types are serialized to XML by accessing their
 * properties using reflection. Support for custom serialization is available by
 * implementing the {@link XMLTypeConverter} interface.
 */
public class XMLConverter {

    private Map<Class<?>, XMLTypeConverter<?>> typeConverters;
    private String dateFormat;
    private String collectionElementName;

    public XMLConverter() {
        typeConverters = new LinkedHashMap<Class<?>, XMLTypeConverter<?>>();
        dateFormat = "yyyy-MM-dd HH:mm:ss";
        collectionElementName = "element";

        registerStandardTypeConverters();
    }

    /**
     * Adds or replaces support for objects of the specified type. The type converter
     * will be used for any object where {@code obj instanceof type}.
     */
    public <T> void registerTypeConverter(Class<? extends T> type, XMLTypeConverter<T> typeConverter) {
        typeConverters.put(type, typeConverter);
    }

    @SuppressWarnings("rawtypes")
    private void registerStandardTypeConverters() {
        registerTypeConverter(Date.class, new XMLTypeConverter<Date>() {
            public Element convertObject(Date obj, String elementName) {
                return XMLHelper.createPropertyElement(elementName, new SimpleDateFormat(dateFormat).format(obj));
            }
        });

        registerTypeConverter(Map.class, new XMLTypeConverter<Map>() {
            public Element convertObject(Map obj, String elementName) {
                Element element = new Element(elementName);
                for (Object key : obj.keySet()) {
                    element.addContent(serializeObject(obj.get(key), key.toString()));
                }
                return element;
            }
        });
    }

    /**
     * Converts the specified object to XML. The object will be serialized using
     * either the provided custom serialization format for the object's type, or
     * the default serialization format if no custom one has been registered.
     * @param rootElementName Will be used as the element name for the root element
     *        in the created XML document.
     * @throws NullPointerException when {@code obj} is {@code null}.
     */
    public Document toXML(Object obj, String rootElementName) {
        if (obj == null || rootElementName == null) {
            throw new NullPointerException();
        }

        Element rootElement = serializeObject(obj, rootElementName);
        return new Document(rootElement);
    }

    private Element serializeObject(Object obj, String elementName) {
        // Use a custom serialization format if one has been registered.
        XMLTypeConverter<Object> customTypeConverter = getTypeConverter(obj);
        if (customTypeConverter != null) {
            return customTypeConverter.convertObject(obj, elementName);
        }

        // Default serialization formats: for complex types, access the 
        // object's properties using reflection. For simple types,
        // serialize the elements to strings.
        if (isSimpleType(obj)) {
            return serializeSimpleType(obj, elementName);
        } else if (isCollectionType(obj)) {
            return serializeCollectionType(obj, elementName);
        } else {
            return serializeComplexTypeUsingReflection(obj, elementName);
        }
    }

    private Element serializeSimpleType(Object obj, String elementName) {
        if (obj == null) {
            return XMLHelper.createPropertyElement(elementName, "");
        }
        return XMLHelper.createPropertyElement(elementName, String.valueOf(obj));
    }

    private Element serializeCollectionType(Object obj, String elementName) {
        Collection<?> collection = null;
        if (obj instanceof Collection<?>) {
            collection = (Collection<?>) obj;
        } else if (obj.getClass().isArray()) {
            collection = convertPrimitiveArrayToList(obj);
        }

        Element element = new Element(elementName);
        for (Object collectionElement : collection) {
            element.addContent(serializeObject(collectionElement, collectionElementName));
        }
        return element;
    }

    private Element serializeComplexTypeUsingReflection(Object obj, String elementName) {
        Element element = new Element(elementName);
        for (Map.Entry<String, Object> prop : ReflectionUtils.getProperties(obj).entrySet()) {
            element.addContent(serializeObject(prop.getValue(), prop.getKey()));
        }
        return element;
    }

    private boolean isSimpleType(Object obj) {
        if (obj == null) {
            return true;
        }
        Class<?> type = obj.getClass();
        return Primitives.isPrimitive(type) || Primitives.isWrapperType(type) || type == String.class;
    }

    private boolean isCollectionType(Object obj) {
        return obj instanceof Collection<?> || obj.getClass().isArray();
    }

    @SuppressWarnings("unchecked")
    private <T> XMLTypeConverter<T> getTypeConverter(T obj) {
        for (Class<?> type : typeConverters.keySet()) {
            if (type.isInstance(obj)) {
                return (XMLTypeConverter<T>) typeConverters.get(type);
            }
        }
        return null;
    }

    private List<Object> convertPrimitiveArrayToList(Object primitiveArray) {
        List<Object> converted = new ArrayList<>();
        for (int i = 0; i < Array.getLength(primitiveArray); i++) {
            converted.add(Array.get(primitiveArray, i));
        }
        return converted;
    }

    public void setDateFormat(String dateFormat) {
        this.dateFormat = dateFormat;
    }

    public String getDateFormat() {
        return dateFormat;
    }

    public void setCollectionElementName(String collectionElementName) {
        this.collectionElementName = collectionElementName;
    }

    public String getCollectionElementName() {
        return collectionElementName;
    }
}