Vidage des propriétés d’un object Java

Existe-t-il une bibliothèque qui videra / imprimera récursivement les propriétés d’un object? Je cherche quelque chose de similaire à la fonction console.dir () dans Firebug.

Je connais le commons-lang ReflectionToSsortingngBuilder mais il ne rentre pas dans un object. Ie, si je lance ce qui suit:

public class ToSsortingng { public static void main(Ssortingng [] args) { System.out.println(ReflectionToSsortingngBuilder.toSsortingng(new Outer(), ToSsortingngStyle.MULTI_LINE_STYLE)); } private static class Outer { private int intValue = 5; private Inner innerValue = new Inner(); } private static class Inner { private Ssortingng ssortingngValue = "foo"; } } 

Je reçois:

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

Je me rends compte que dans mon exemple, j’aurais pu remplacer la méthode toSsortingng () pour Inner mais dans le monde réel, je traite des objects externes que je ne peux pas modifier.

Vous pouvez essayer 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   

Vous pouvez également produire en JSON

Et faites attention aux références circulaires;)

J’ai essayé d’utiliser XStream comme suggéré à l’origine, mais il s’avère que le graphe d’object que je voulais dump comportait une référence au marshaller XStream lui-même, ce qui n’était pas très utile (pourquoi il doit lancer une exception plutôt que de l’ignorer ou enregistrer un bon avertissement, je ne suis pas sûr.)

J’ai ensuite essayé le code de user519500 ci-dessus mais j’ai trouvé que j’avais besoin de quelques modifications. Voici un cours que vous pouvez intégrer dans un projet qui offre les fonctionnalités supplémentaires suivantes:

  • Peut contrôler la profondeur de récursivité maximale
  • Peut limiter la sortie des éléments du tableau
  • Peut ignorer toute liste de classes, champs ou combinaisons de champs de classe + – il suffit de passer un tableau avec une combinaison de noms de classes, de paires classname + fieldname séparés par deux points ou de noms de champs avec un préfixe colon, à savoir: [][:]
  • Ne produira pas le même object deux fois (la sortie indique quand un object a déjà été visité et fournit le code de hachage pour la corrélation) – cela évite les références circulaires qui causent des problèmes

Vous pouvez appeler ceci en utilisant l’une des deux méthodes ci-dessous:

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

Comme mentionné ci-dessus, vous devez faire attention aux débordements de stack avec ceci, utilisez donc la fonction de profondeur maximale de récursivité pour minimiser le risque.

J’espère que quelqu’un trouvera cela utile!

 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 Ssortingng dump(Object o) { return dump(o, 0, 0, null); } public static Ssortingng dump(Object o, int maxDepth, int maxArrayElements, Ssortingng[] 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++; SsortingngBuffer tabs = new SsortingngBuffer(); 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.toSsortingng().subssortingng(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.toSsortingng().subssortingng(1)); buffer.append("}"); } ctx.callCount--; return buffer.toSsortingng(); } protected static Ssortingng 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.Ssortingng.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.toSsortingng(); } 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 Ssortingng getSimpleNameWithoutArrayQualifier(Class clazz) { Ssortingng simpleName = clazz.getSimpleName(); int indexOfBracket = simpleName.indexOf('['); if (indexOfBracket != -1) return simpleName.subssortingng(0, indexOfBracket); return simpleName; } } 

Vous pouvez utiliser ReflectionToSsortingngBuilder avec un ToSsortingngStyle personnalisé, par exemple:

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

Et puis vous l’invoquez comme:

 ReflectionToSsortingngBuilder.toSsortingng(value, MyStyle.getInstance()); 

Attention aux références circulaires!


Vous pouvez également utiliser json-lib ( http://json-lib.sourceforge.net ) et faire simplement:

 JSONObject.fromObject(value); 

Cela permet d’imprimer tous les champs (y compris les tableaux d’objects) d’un object.

Version corrigée du post de Ben Williams de ce sujet

Note: cette méthode utilise la récursivité, donc si vous avez un graphe d’object très profond, vous pouvez obtenir un débordement de stack (sans jeu de mots;) SI vous devez donc utiliser le paramètre VM -Xss10m. Si vous utilisez eclipse, mettez-le en exécution> runconfiguration> augmente (onglet) la zone d’extension VM et appuyez sur Appliquer

 import java.lang.reflect.Array; import java.lang.reflect.Field; public static Ssortingng dump(Object o) { SsortingngBuffer buffer = new SsortingngBuffer(); 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(); } 

Je voulais une solution élégante à ce problème:

  • N’utilise aucune bibliothèque externe
  • Utilise Reflection pour accéder aux champs, y compris aux champs de superclasses
  • Utilise la récursivité pour parcourir le graphe d’objects avec un seul cadre de stack par appel
  • Utilise un IdentityHashMap pour gérer les références en arrière et éviter une récursion infinie
  • Traite correctement les primitives, la mise en boîte automatique, les CharSequences, les énumérations et les valeurs null
  • Vous permet de choisir d’parsingr ou non les champs statiques
  • Est-ce assez simple à modifier en fonction des préférences de formatage

J’ai écrit la classe d’utilitaire suivante:

 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 ssortingng using reflection and recursion. */ public class SsortingngDump { /** * 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 ssortingng representing the internal values of the parsed object */ public static Ssortingng 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 Ssortingng#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 ssortingng representing the internal values of the parsed object */ public static Ssortingng dump(Object object, boolean isIncludingStatics) { return dump(object, isIncludingStatics, new IdentityHashMap(), 0); } private static Ssortingng 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 Ssortingng.valueOf(object); } SsortingngBuilder builder = new SsortingngBuilder(); 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); SsortingngBuilder tabs = new SsortingngBuilder(); 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 SsortingngBuilder superPrefix = new SsortingngBuilder(); 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()) { Ssortingng name = entry.getKey(); Field field = entry.getValue(); Ssortingng dump; try { boolean wasAccessible = field.isAccessible(); field.setAccessible(true); Object fieldObject = field.get(object); field.setAccessible(wasAccessible); // the accessibility flag should be restored 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.toSsortingng(); } }

Je l’ai testé sur plusieurs classes et pour moi c’est extrêmement efficace. Par exemple, essayez de l’utiliser pour vider le thread principal:

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

modifier

Depuis l’écriture de cet article, j’ai eu raison de créer une version itérative de cet algorithme. La version récursive est limitée en profondeur par les frameworks de stack totaux, mais vous pourriez avoir des raisons de vider un graphe d’objects extrêmement grand. Pour gérer ma situation, j’ai révisé l’algorithme pour utiliser une structure de données de stack à la place de la stack d’exécution. Cette version est efficace dans le temps et est limitée par la taille de segment au lieu de la profondeur de trame de stack.

Vous pouvez télécharger et utiliser la version itérative ici .

Vous devez utiliser RecursiveToSsortingngStyle:

 System.out.println(ReflectionToSsortingngBuilder.toSsortingng(new Outer(), new RecursiveToSsortingngStyle())); 

Vous pourriez peut-être utiliser un cadre de liaison XML tel que XStream , Digester ou JAXB pour cela.

Vous pouvez utiliser Gson pour représenter votre object au format json:

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

Ne fonctionne pas pour les objects Map avec d’autres clés que Ssortingng. Peut-être que JsonConfig peut gérer cela.

Je vous recommande d’utiliser le GSON Lib for Java.

Si vous utilisez Maven, vous pouvez l’utiliser.

Ou vous pouvez télécharger le fichier Jar ici .

Voici un exemple d’utilisation:

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