Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dumping a java object's properties

Is there a library that will recursively dump/print an objects properties? I'm looking for something similar to the console.dir() function in Firebug.

I'm aware of the commons-lang ReflectionToStringBuilder but it does not recurse into an object. I.e., if I run the following:

public class ToString {      public static void main(String [] args) {         System.out.println(ReflectionToStringBuilder.toString(new Outer(), ToStringStyle.MULTI_LINE_STYLE));     }      private static class Outer {         private int intValue = 5;         private Inner innerValue = new Inner();     }      private static class Inner {         private String stringValue = "foo";     } } 

I receive:

ToString$Outer@1b67f74[ intValue=5
innerValue=ToString$Inner@530daa ]

I realize that in my example, I could have overriden the toString() method for Inner but in the real world, I'm dealing with external objects that I can't modify.

like image 382
Kevin Avatar asked Mar 02 '09 16:03

Kevin


People also ask

How do I remove a property in Java?

The Java. util. Properties. clear() method is used to remove all the elements from this Properties instance.

What are the properties of an object in Java?

An object has three characteristics: State: represents the data (value) of an object. Behavior: represents the behavior (functionality) of an object such as deposit, withdraw, etc. Identity: An object identity is typically implemented via a unique ID.

How do you log an object in Java?

Logger log() Method in Java with Examples. The log() method of Logger is used to Log a message. If the logger is currently enabled for the given message level which is passed as parameter then a corresponding LogRecord is created and forwarded to all the registered Output Handler objects.


2 Answers

You could try XStream.

XStream xstream = new XStream(new Sun14ReflectionProvider(   new FieldDictionary(new ImmutableFieldKeySorter())),   new DomDriver("utf-8")); System.out.println(xstream.toXML(new Outer())); 

prints out:

<foo.ToString_-Outer>   <intValue>5</intValue>   <innerValue>     <stringValue>foo</stringValue>   </innerValue> </foo.ToString_-Outer> 

You could also output in JSON

And be careful of circular references ;)

like image 177
cherouvim Avatar answered Sep 23 '22 20:09

cherouvim


I tried using XStream as originally suggested, but it turns out the object graph I wanted to dump included a reference back to the XStream marshaller itself, which it didn't take too kindly to (why it must throw an exception rather than ignoring it or logging a nice warning, I'm not sure.)

I then tried out the code from user519500 above but found I needed a few tweaks. Here's a class you can roll into a project that offers the following extra features:

  • Can control max recursion depth
  • Can limit array elements output
  • Can ignore any list of classes, fields, or class+field combinations - just pass an array with any combination of class names, classname+fieldname pairs separated with a colon, or fieldnames with a colon prefix ie: [<classname>][:<fieldname>]
  • Will not output the same object twice (the output indicates when an object was previously visited and provides the hashcode for correlation) - this avoids circular references causing problems

You can call this using one of the two methods below:

    String dump = Dumper.dump(myObject);     String dump = Dumper.dump(myObject, maxDepth, maxArrayElements, ignoreList); 

As mentioned above, you need to be careful of stack-overflows with this, so use the max recursion depth facility to minimise the risk.

Hopefully somebody will find this useful!

package com.mycompany.myproject;  import java.lang.reflect.Array; import java.lang.reflect.Field; import java.util.HashMap;  public class Dumper {     private static Dumper instance = new Dumper();      protected static Dumper getInstance() {         return instance;     }      class DumpContext {         int maxDepth = 0;         int maxArrayElements = 0;         int callCount = 0;         HashMap<String, String> ignoreList = new HashMap<String, String>();         HashMap<Object, Integer> visited = new HashMap<Object, Integer>();     }      public static String dump(Object o) {         return dump(o, 0, 0, null);     }      public static String dump(Object o, int maxDepth, int maxArrayElements, String[] ignoreList) {         DumpContext ctx = Dumper.getInstance().new DumpContext();         ctx.maxDepth = maxDepth;         ctx.maxArrayElements = maxArrayElements;          if (ignoreList != null) {             for (int i = 0; i < Array.getLength(ignoreList); i++) {                 int colonIdx = ignoreList[i].indexOf(':');                 if (colonIdx == -1)                     ignoreList[i] = ignoreList[i] + ":";                 ctx.ignoreList.put(ignoreList[i], ignoreList[i]);             }         }          return dump(o, ctx);     }      protected static String dump(Object o, DumpContext ctx) {         if (o == null) {             return "<null>";         }          ctx.callCount++;         StringBuffer tabs = new StringBuffer();         for (int k = 0; k < ctx.callCount; k++) {             tabs.append("\t");         }         StringBuffer buffer = new StringBuffer();         Class oClass = o.getClass();          String oSimpleName = getSimpleNameWithoutArrayQualifier(oClass);          if (ctx.ignoreList.get(oSimpleName + ":") != null)             return "<Ignored>";          if (oClass.isArray()) {             buffer.append("\n");             buffer.append(tabs.toString().substring(1));             buffer.append("[\n");             int rowCount = ctx.maxArrayElements == 0 ? Array.getLength(o) : Math.min(ctx.maxArrayElements, Array.getLength(o));             for (int i = 0; i < rowCount; i++) {                 buffer.append(tabs.toString());                 try {                     Object value = Array.get(o, i);                     buffer.append(dumpValue(value, ctx));                 } catch (Exception e) {                     buffer.append(e.getMessage());                 }                 if (i < Array.getLength(o) - 1)                     buffer.append(",");                 buffer.append("\n");             }             if (rowCount < Array.getLength(o)) {                 buffer.append(tabs.toString());                 buffer.append(Array.getLength(o) - rowCount + " more array elements...");                 buffer.append("\n");             }             buffer.append(tabs.toString().substring(1));             buffer.append("]");         } else {             buffer.append("\n");             buffer.append(tabs.toString().substring(1));             buffer.append("{\n");             buffer.append(tabs.toString());             buffer.append("hashCode: " + o.hashCode());             buffer.append("\n");             while (oClass != null && oClass != Object.class) {                 Field[] fields = oClass.getDeclaredFields();                  if (ctx.ignoreList.get(oClass.getSimpleName()) == null) {                     if (oClass != o.getClass()) {                         buffer.append(tabs.toString().substring(1));                         buffer.append("  Inherited from superclass " + oSimpleName + ":\n");                     }                      for (int i = 0; i < fields.length; i++) {                          String fSimpleName = getSimpleNameWithoutArrayQualifier(fields[i].getType());                         String fName = fields[i].getName();                          fields[i].setAccessible(true);                         buffer.append(tabs.toString());                         buffer.append(fName + "(" + fSimpleName + ")");                         buffer.append("=");                          if (ctx.ignoreList.get(":" + fName) == null &&                             ctx.ignoreList.get(fSimpleName + ":" + fName) == null &&                             ctx.ignoreList.get(fSimpleName + ":") == null) {                              try {                                 Object value = fields[i].get(o);                                 buffer.append(dumpValue(value, ctx));                             } catch (Exception e) {                                 buffer.append(e.getMessage());                             }                             buffer.append("\n");                         }                         else {                             buffer.append("<Ignored>");                             buffer.append("\n");                         }                     }                     oClass = oClass.getSuperclass();                     oSimpleName = oClass.getSimpleName();                 }                 else {                     oClass = null;                     oSimpleName = "";                 }             }             buffer.append(tabs.toString().substring(1));             buffer.append("}");         }         ctx.callCount--;         return buffer.toString();     }      protected static String dumpValue(Object value, DumpContext ctx) {         if (value == null) {             return "<null>";         }         if (value.getClass().isPrimitive() ||             value.getClass() == java.lang.Short.class ||             value.getClass() == java.lang.Long.class ||             value.getClass() == java.lang.String.class ||             value.getClass() == java.lang.Integer.class ||             value.getClass() == java.lang.Float.class ||             value.getClass() == java.lang.Byte.class ||             value.getClass() == java.lang.Character.class ||             value.getClass() == java.lang.Double.class ||             value.getClass() == java.lang.Boolean.class ||             value.getClass() == java.util.Date.class ||             value.getClass().isEnum()) {              return value.toString();          } else {              Integer visitedIndex = ctx.visited.get(value);             if (visitedIndex == null) {                 ctx.visited.put(value, ctx.callCount);                 if (ctx.maxDepth == 0 || ctx.callCount < ctx.maxDepth) {                     return dump(value, ctx);                 }                 else {                     return "<Reached max recursion depth>";                 }             }             else {                 return "<Previously visited - see hashCode " + value.hashCode() + ">";             }         }     }       private static String getSimpleNameWithoutArrayQualifier(Class clazz) {         String simpleName = clazz.getSimpleName();         int indexOfBracket = simpleName.indexOf('[');          if (indexOfBracket != -1)             return simpleName.substring(0, indexOfBracket);         return simpleName;     } } 
like image 40
John Rix Avatar answered Sep 20 '22 20:09

John Rix