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.
The Java. util. Properties. clear() method is used to remove all the elements from this Properties instance.
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.
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.
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 ;)
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:
[<classname>][:<fieldname>]
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; } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With