A simple class for converting any Java object to XML string

In need to save XML representation of your Java object Here is a simple 200-line class that will do this using reflection. But don`t worry, there is some very powerful caching going on, so that the performance will be very good. 

 // OptimizedReflectionMarshaller.java

package my;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.output.XMLOutputter;

/*
 * This is a utility class that marshals a Java Object into an XML
 * String.  Originally, this used StringBuffer to build the XML
 * String.  However, it has been modified from it's original
 * version to build the XML with JDOM instead.
 *
 * @author Kirill at http://www.topxml.com/rbnews/XML/re-2909_A-simple-class-for-converting-any-Java-object-to-XML-string.aspx
 *
 * @date January 21, 2008 – modified
 * @author Jimmy Honeycutt – modified to use JDOM to create XML instead of StringBuffer.
 
 */
public class OptimizedReflectionMarshaller {
    // cache for getters
    private static HashMap gettersMap = new HashMap();

    // cache for storing info on whether certain class implements Collection
    private static HashMap collectionsMap = new HashMap();

    private static final String JAVA = "java.";
    private static final String JAVAX = "javax.";
 
    private static final Class[] EMPTYPARAMS = new Class[0];
 
    /**
     * Info on a single field and the corresponding getter method
     */
    private static class FieldMethodPair {
        
        private String fieldName;

        private Method getterMethod;

        public FieldMethodPair(String fieldName, Method getterMethod) {
            this.fieldName = fieldName;
            this.getterMethod = getterMethod;
        }

        public String getFieldName() {
            return fieldName;
        }

        public Method getGetterMethod() {
            return getterMethod;
        }
    }

    /**
     * Returns the marshalled XML representation of the parameter object
     * @param obj the Java Object to be marshalled to XML
     * @throws IOException – thrown by JDOM
     * @throws IllegalAccesException – thrown by reflection
     * @throws InvocationTargetException – thrown by reflection
     */
    public static String marshal(Object obj)
    throws IOException, IllegalAccessException, InvocationTargetException {
        Class clazz = obj.getClass();

        // get class name without package name (i.e. com.acme.springapp.view.Product = Product)
        String className = clazz.getName();
    
        // root node name =
        Element rootEl = new Element(formatNodeName(className));
        marshal(obj, rootEl);
    
        Document doc = new Document(rootEl);
        return new XMLOutputter().outputString(doc);
    }

      /**
     * Returns getter function for the specified field
     * @param clazz the Java Object's reflection Class
     * @param fieldName the field name (or attribute) in the object
        */
      private static Method getGetter(Class clazz, String fieldName) {
          try {
              // for example, for `name` we will look for `getName`
              String getMethodName = fieldName.substring(0, 1);
              getMethodName = getMethodName.toUpperCase();
              getMethodName = "get" + getMethodName + fieldName.substring(1, fieldName.length());
              Method getMethod = clazz.getMethod(getMethodName, EMPTYPARAMS);
              return getMethod;
          } catch (NoSuchMethodException nsme) {
              return null;
          }
      }

      /**
     * Returns a list of all fields that have getters
     * @param clazz the Java Object's reflection Class
        */
      private static List getAllGetters(Class clazz) {
          try {
              // check if have in cache
              if (gettersMap.containsKey(clazz.getName()))
                  return (List) gettersMap.get(clazz.getName());

                  List fieldList = new LinkedList();
                  Class currClazz = clazz;
                  while (true) {
                      Field[] fields = currClazz.getDeclaredFields();
                      for (int i = 0; i < fields.length; i++) {
                          Field currField = fields[i];
                          int modifiers = currField.getModifiers();
                          // check if not static and has getter
                          if (!Modifier.isStatic(modifiers)) {
                              Method getterMethod = getGetter(clazz, currField.getName());
                              if (getterMethod != null) {
                                  FieldMethodPair fmp = new FieldMethodPair(currField.getName(), getterMethod);
                                  fieldList.add(fmp);
                              }
                          }
                      }
                      
                      currClazz = currClazz.getSuperclass();
                      if (currClazz == null) {
                          break;
                      }
                  }

                  // store in cache
                  gettersMap.put(clazz.getName(), fieldList);

                  return fieldList;
          } catch (Exception exc) {
              exc.printStackTrace();
              return null;
          }
      }

      /**
       * Checks whether the specified class implements Collection interface
     * @param class the Java Object's reflection Class
     */
      private static boolean isImplementsCollection(Class clazz) {
          String className = clazz.getName();
          // check in cache
          if (collectionsMap.containsKey(className)) {
              return ((Boolean) collectionsMap.get(className)).booleanValue();
          }

        boolean result = Collection.class.isAssignableFrom(clazz);

        // store in cache
        collectionsMap.put(className, new Boolean(result));
        return result;
      }
      
      /**
       * Format and append the data within object to JDOM element.
       * @param obj the Java object
       * @param el the jdom element
       */
      private static void appendFormatted(Object obj, Element el) {
          String strRepresentation = obj.toString();
          int len = strRepresentation.length();
    
          StringBuffer sb = new StringBuffer();
          for (int i = 0; i < len; i++) {
              char c = strRepresentation.charAt(i);
              switch (c) {
                  case '&':
                      sb.append("&");
                      break;
                  case '<':
                      sb.append("<");
                      break;
                  case '>':
                      sb.append(">");
                      break;
                  case '\'':
                      sb.append("'");
                      break;
                  case '\"':
                      sb.append("\"");
                      break;
                  default:
                      sb.append(c);
              }
        }
    
        el.addContent(sb.toString());
      }
      
      /**
       * Marshalls the Java Object and appends data to
       * the XML JDOM root element.
       * @param obj the Java Object
       * @param root the root element
       * @throws IllegalAccessException
       * @throws InvocationTargetException
       */
      private static void marshal(Object obj, Element root)
    throws IllegalAccessException, InvocationTargetException {
          Class clazz = obj.getClass();
        String className = clazz.getName();

        // check if implements Collection
        if (isImplementsCollection(clazz)) {
            Collection cobj = (Collection) obj;
              Iterator it = cobj.iterator();
              while (it.hasNext()) {
                  Object eobj = it.next();
                Element childEl = new Element(formatNodeName(eobj.getClass().getName()));
                marshal(eobj, childEl);
                root.addContent(childEl);
              }
              return;
        }

        // check for primitive types  
        if (className.startsWith(JAVA) || className.startsWith(JAVAX)) {
            appendFormatted(obj, root);
              return;
        }
 
        // otherwise put all fields with getters
        List allGetters = getAllGetters(clazz);
        Iterator itGetters = allGetters.iterator();
        while (itGetters.hasNext()) {
            FieldMethodPair currGetter = (FieldMethodPair) itGetters.next();
              String currFieldName = currGetter.fieldName;
              Method currentGetter = currGetter.getterMethod;
 
              // call method
              Object val = currentGetter.invoke(obj, EMPTYPARAMS);
              if (val != null) {
               Element fieldEl = new Element(formatNodeName(currFieldName));
                // call recursively
                marshal(val, fieldEl);
                root.addContent(fieldEl);
              }
        }
      }
 
      /**
        * Formats an XML node name (i.e. <Product></Product>) by removing
        * the package name (if exists) and making first letter upper-case.
        * @param name the name of the class or attribute for the XML node name
        * @return String
        */
      private static String formatNodeName(String name) {
          // remove lengthy package name
          int startIndex = name.lastIndexOf(".");  
          
          if(startIndex > 0) {
              name = name.substring(startIndex + 1, name.length());
          }
          
          // upper-case the first character
          String firstChar = name.substring(0,1).toUpperCase();
      
          return firstChar + name.substring(1);
      }
}

5 Replies to “A simple class for converting any Java object to XML string”

  1. This is really neat (and fast too!)

    Would it be feasible to create an ‘unmarshal’ function, that would do the opposite, based on the class instance? Something along:

    MyObject obj = (MyObject) OptimizedReflectionMarshaller.unmarshal(MyObject.class, xmlString);

    This would be really nice…

  2. Thnx much for this post. Really helpful.
    While using this class, if anybody gets the exception “java.lang.IllegalAccessException: Class *************.OptimizedReflectionMarshaller can not access a member of class org.apache.xerces.jaxp.datatype.XMLGregorianCalendarImpl with modifiers “public””, then you just have to do a runtime check and convert xerces XMLGregorianCalendarImpl to javax.xml.datatype.XMLGregorianCalendar.
    In marshal(Object obj, Element root), I did :
    if(!obj.getClass().getName().equals(“org.apache.xerces.jaxp.datatype.XMLGregorianCalendarImpl”)){
    clazz = obj.getClass();
    }
    else {
    clazz = XMLGregorianCalendar.class;
    }
    Further, also keep in mind that this class generates the getter as per ideal java naming format (ex – for field abc, generated getter will be getAbc)
    So if any of your fields has getter like getABC, it will be ignored w/o any errors though.
    That has to be explicitly handled in getGetter(Class, String) method.

Leave a Reply to E. Johnson Cancel reply

Your email address will not be published. Required fields are marked *