Volcar las propiedades de un objeto java

¿Hay una biblioteca que volcará / imprimirá recursivamente las propiedades de un objeto? Estoy buscando algo similar a la función console.dir () en Firebug.

Soy consciente del common-lang ReflectionToStringBuilder pero no se repite en un objeto. Es decir, si ejecuto lo siguiente:

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

Recibo:

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

Me doy cuenta de que en mi ejemplo, podría haber anulado el método toString () para Inner, pero en el mundo real, estoy tratando con objetos externos que no puedo modificar.

Podrías probar con XStream .

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

imprime:

  5  foo   

También podría generar en JSON

Y tenga cuidado con las referencias circulares;)

Intenté usar XStream como se sugirió originalmente, pero resulta que el gráfico de objetos que quería volcar incluía una referencia de regreso al marshaller XStream en sí mismo, que no tuvo en cuenta muy amablemente (por qué debe lanzar una excepción en lugar de ignorarla o registrando una buena advertencia, no estoy seguro.)

Luego probé el código del usuario519500 anterior, pero descubrí que necesitaba algunos ajustes. Aquí hay una clase en la que puede participar en un proyecto que ofrece las siguientes características adicionales:

  • Puede controlar la profundidad máxima de recursión
  • Puede limitar la salida de elementos de la matriz
  • Puede ignorar cualquier lista de clases, campos o combinaciones de clase + campo: simplemente pase una matriz con cualquier combinación de nombres de clase, nombre de clase + nombre de campo separados por dos puntos, o nombres de campo con un prefijo de dos puntos, es decir: [][:]
  • No generará el mismo objeto dos veces (la salida indica cuando un objeto fue visitado previamente y proporciona el código hash para la correlación) – esto evita las referencias circulares que causan problemas

Puede llamar esto usando uno de los dos métodos a continuación:

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

Como se mencionó anteriormente, debe tener cuidado con los desbordamientos de stack con esto, así que use la función de profundidad de recursión máxima para minimizar el riesgo.

¡Espero que alguien lo encuentre útil!

 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 ignoreList = new HashMap(); HashMap visited = new HashMap(); } 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 ""; } 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 ""; 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(""); 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 ""; } 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 ""; } } else { return ""; } } } private static String getSimpleNameWithoutArrayQualifier(Class clazz) { String simpleName = clazz.getSimpleName(); int indexOfBracket = simpleName.indexOf('['); if (indexOfBracket != -1) return simpleName.substring(0, indexOfBracket); return simpleName; } } 

Puede usar ReflectionToStringBuilder con un ToStringStyle personalizado, por ejemplo:

 class MyStyle extends ToStringStyle { private final static ToStringStyle instance = new MyStyle(); public MyStyle() { setArrayContentDetail(true); setUseShortClassName(true); setUseClassName(false); setUseIdentityHashCode(false); setFieldSeparator(", " + SystemUtils.LINE_SEPARATOR + " "); } public static ToStringStyle getInstance() { return instance; }; @Override public void appendDetail(StringBuffer buffer, String fieldName, Object value) { if (!value.getClass().getName().startsWith("java")) { buffer.append(ReflectionToStringBuilder.toString(value, instance)); } else { super.appendDetail(buffer, fieldName, value); } } @Override public void appendDetail(StringBuffer buffer, String fieldName, Collection value) { appendDetail(buffer, fieldName, value.toArray()); } } 

Y luego lo invocas como:

 ReflectionToStringBuilder.toString(value, MyStyle.getInstance()); 

Sin embargo, ¡ten cuidado con las referencias circulares!


También puede usar json-lib ( http://json-lib.sourceforge.net ) y simplemente hacer:

 JSONObject.fromObject(value); 

esto imprimirá todos los campos (incluidas las matrices de objetos) de un objeto.

Versión fija de la publicación de Ben Williams de este hilo

Nota: este método usa la recursividad por lo que si tiene un gráfico de objeto muy profundo puede obtener un desbordamiento de stack (sin juego de palabras;) SI es así, debe usar el parámetro VM -Xss10m. Si su eclipse de uso lo puso en ejecución> configuración de ejecución> aumenta el cuadro de aumento VM y presione aplicar

 import java.lang.reflect.Array; import java.lang.reflect.Field; public static String dump(Object o) { StringBuffer buffer = new StringBuffer(); Class oClass = o.getClass(); if (oClass.isArray()) { buffer.append("Array: "); buffer.append("["); for (int i = 0; i < Array.getLength(o); i++) { Object value = Array.get(o, i); if (value.getClass().isPrimitive() || value.getClass() == java.lang.Long.class || value.getClass() == java.lang.Integer.class || value.getClass() == java.lang.Boolean.class || value.getClass() == java.lang.String.class || value.getClass() == java.lang.Double.class || value.getClass() == java.lang.Short.class || value.getClass() == java.lang.Byte.class ) { buffer.append(value); if(i != (Array.getLength(o)-1)) buffer.append(","); } else { buffer.append(dump(value)); } } buffer.append("]\n"); } else { buffer.append("Class: " + oClass.getName()); buffer.append("{\n"); while (oClass != null) { Field[] fields = oClass.getDeclaredFields(); for (int i = 0; i < fields.length; i++) { fields[i].setAccessible(true); buffer.append(fields[i].getName()); buffer.append("="); try { Object value = fields[i].get(o); if (value != null) { if (value.getClass().isPrimitive() || value.getClass() == java.lang.Long.class || value.getClass() == java.lang.String.class || value.getClass() == java.lang.Integer.class || value.getClass() == java.lang.Boolean.class || value.getClass() == java.lang.Double.class || value.getClass() == java.lang.Short.class || value.getClass() == java.lang.Byte.class ) { buffer.append(value); } else { buffer.append(dump(value)); } } } catch (IllegalAccessException e) { buffer.append(e.getMessage()); } buffer.append("\n"); } oClass = oClass.getSuperclass(); } buffer.append("}\n"); } return buffer.toString(); } 

Quería una solución elegante a este problema que:

  • No usa ninguna biblioteca externa
  • Utiliza Reflexión para acceder a los campos, incluidos los campos de superclase
  • Utiliza recursividad para atravesar el Object-graph con solo un marco de stack por llamada
  • Utiliza un IdentityHashMap para manejar las referencias hacia atrás y evitar la recursión infinita
  • Maneja primitivas, auto-boxing, CharSequences, enumeraciones y nulos apropiadamente
  • Le permite elegir si desea analizar o no los campos estáticos
  • Es lo suficientemente simple como para modificar de acuerdo con las preferencias de formato

Escribí la siguiente clase de utilidad:

 import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.IdentityHashMap; import java.util.Map.Entry; import java.util.TreeMap; /** * Utility class to dump {@code Object}s to string using reflection and recursion. */ public class StringDump { /** * Uses reflection and recursion to dump the contents of the given object using a custom, JSON-like notation (but not JSON). Does not format static fields.

* @see #dump(Object, boolean, IdentityHashMap, int) * @param object the {@code Object} to dump using reflection and recursion * @return a custom-formatted string representing the internal values of the parsed object */ public static String dump(Object object) { return dump(object, false, new IdentityHashMap(), 0); } /** * Uses reflection and recursion to dump the contents of the given object using a custom, JSON-like notation (but not JSON).

* Parses all fields of the runtime class including super class fields, which are successively prefixed with "{@code super.}" at each level.

* {@code Number}s, {@code enum}s, and {@code null} references are formatted using the standard {@link String#valueOf()} method. * {@code CharSequences}s are wrapped with quotes.

* The recursive call invokes only one method on each recursive call, so limit of the object-graph depth is one-to-one with the stack overflow limit.

* Backwards references are tracked using a "visitor map" which is an instance of {@link IdentityHashMap}. * When an existing object reference is encountered the {@code "sysId"} is printed and the recursion ends.

* * @param object the {@code Object} to dump using reflection and recursion * @param isIncludingStatics {@code true} if {@code static} fields should be dumped, {@code false} to skip them * @return a custom-formatted string representing the internal values of the parsed object */ public static String dump(Object object, boolean isIncludingStatics) { return dump(object, isIncludingStatics, new IdentityHashMap(), 0); } private static String dump(Object object, boolean isIncludingStatics, IdentityHashMap visitorMap, int tabCount) { if (object == null || object instanceof Number || object instanceof Character || object instanceof Boolean || object.getClass().isPrimitive() || object.getClass().isEnum()) { return String.valueOf(object); } StringBuilder builder = new StringBuilder(); int sysId = System.identityHashCode(object); if (object instanceof CharSequence) { builder.append("\"").append(object).append("\""); } else if (visitorMap.containsKey(object)) { builder.append("(sysId#").append(sysId).append(")"); } else { visitorMap.put(object, object); StringBuilder tabs = new StringBuilder(); for (int t = 0; t < tabCount; t++) { tabs.append("\t"); } if (object.getClass().isArray()) { builder.append("[").append(object.getClass().getName()).append(":sysId#").append(sysId); int length = Array.getLength(object); for (int i = 0; i < length; i++) { Object arrayObject = Array.get(object, i); String dump = dump(arrayObject, isIncludingStatics, visitorMap, tabCount + 1); builder.append("\n\t").append(tabs).append("\"").append(i).append("\":").append(dump); } builder.append(length == 0 ? "" : "\n").append(length == 0 ? "" : tabs).append("]"); } else { // enumerate the desired fields of the object before accessing TreeMap fieldMap = new TreeMap(); // can modify this to change or omit the sort order StringBuilder superPrefix = new StringBuilder(); for (Class< ?> clazz = object.getClass(); clazz != null && !clazz.equals(Object.class); clazz = clazz.getSuperclass()) { Field[] fields = clazz.getDeclaredFields(); for (int i = 0; i < fields.length; i++) { Field field = fields[i]; if (isIncludingStatics || !Modifier.isStatic(field.getModifiers())) { fieldMap.put(superPrefix + field.getName(), field); } } superPrefix.append("super."); } builder.append("{").append(object.getClass().getName()).append(":sysId#").append(sysId); for (Entry entry : fieldMap.entrySet()) { String name = entry.getKey(); Field field = entry.getValue(); String dump; try { boolean wasAccessible = field.isAccessible(); field.setAccessible(true); Object fieldObject = field.get(object); field.setAccessible(wasAccessible); // the accessibility flag should be restred to its prior ClassLoader state dump = dump(fieldObject, isIncludingStatics, visitorMap, tabCount + 1); } catch (Throwable e) { dump = "!" + e.getClass().getName() + ":" + e.getMessage(); } builder.append("\n\t").append(tabs).append("\"").append(name).append("\":").append(dump); } builder.append(fieldMap.isEmpty() ? "" : "\n").append(fieldMap.isEmpty() ? "" : tabs).append("}"); } } return builder.toString(); } }

Lo probé en varias clases y para mí es extremadamente eficiente. Por ejemplo, intente usarlo para volcar el hilo principal:

 public static void main(String[] args) throws Exception { System.out.println(dump(Thread.currentThread())); } 

Editar

Desde que escribí esta publicación tuve razones para crear una versión iterativa de este algoritmo. La versión recursiva está limitada en profundidad por los cuadros de stack totales, pero es posible que tenga motivos para volcar un gráfico de objetos extremadamente grande. Para manejar mi situación, revisé el algoritmo para usar una estructura de datos de stack en lugar de la stack de tiempo de ejecución. Esta versión es eficiente en tiempo y está limitada por el tamaño del montón en lugar de la profundidad del marco de la stack.

Puede descargar y usar la versión iterativa aquí .

Deberías usar RecursiveToStringStyle:

 System.out.println(ReflectionToStringBuilder.toString(new Outer(), new RecursiveToStringStyle())); 

Tal vez podrías usar un marco de enlace XML como XStream , Digester o JAXB para eso.

Puede usar Gson para representar su objeto en formato json:

 new GsonBuilder().setPrettyPrinting().create().toJson(yourObject); 
 JSONObject.fromObject(value) 

No funciona para objetos Map con otras teclas que String. Quizás JsonConfig puede manejar esto.

Te recomiendo usar GSON Lib para Java.

si usa Maven puede usar esto .

O puede descargar el archivo Jar desde aquí .

Aquí un ejemplo de cómo usarlo:

 Gson gson = new GsonBuilder().setPrettyPrinting().create(); String json = gson.toJson(obj); System.out.println(json);