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);
      }
}

This entry was posted in Articles, Java. Bookmark the permalink.

5 Responses to A simple class for converting any Java object to XML string

  1. E. Johnson says:

    Thanks very much!!

  2. JP Robichaud says:

    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…

  3. anon says:

    have you looked at vtd-xml, which is the latest and more advanced/powerful XML Processing API

    vtd-xml

  4. nesh says:

    really very helpful. thanks!!

  5. hrishi says:

    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

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