wake-up-neo.net

Ausgeben der Eigenschaften eines Java-Objekts

Gibt es eine Bibliothek, die Objekteigenschaften rekursiv ausgeben oder drucken kann? Ich suche etwas Ähnliches zu der Funktion console.dir () in Firebug.

Ich bin mir des commons-lang ReflectionToStringBuilder bewusst, aber es kommt nicht zu einem Objekt. Das heißt, wenn ich Folgendes ausführen:

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

Ich erhalte:

ToString $ Outer @ 1b67f74 [ intValue = 5
[email protected] ]

Ich weiß, dass ich in meinem Beispiel die toString () - Methode für Inner hätte überschreiben können, aber in der realen Welt beschäftige ich mich mit externen Objekten, die ich nicht ändern kann.

63
Kevin

Sie könnten XStream versuchen.

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

druckt aus:

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

Sie können auch in JSON ausgeben.

Und achten Sie auf Zirkelverweise;)

41
cherouvim

Ich habe versucht, XStream wie ursprünglich vorgeschlagen zu verwenden, aber es stellte sich heraus, dass das Objektdiagramm, das ich ausgeben wollte, einen Verweis auf den XStream-Marshaller selbst enthielt, was nicht zu freundlich war (weshalb es eine Ausnahme auslösen muss, anstatt es zu ignorieren oder Eine schöne Warnung abmelden, ich bin mir nicht sicher.)

Ich habe dann den Code von user519500 oben ausprobiert, aber ich fand ein paar Verbesserungen. Hier ist eine Klasse, die Sie in ein Projekt einrollen können, das die folgenden zusätzlichen Funktionen bietet:

  • Kann die maximale Rekursionstiefe steuern
  • Kann die Ausgabe von Array-Elementen begrenzen
  • Kann jede Liste von Klassen, Feldern oder Klassen + Feldkombinationen ignorieren. Übergeben Sie einfach ein Array mit einer Kombination aus Klassennamen, Klassennamen + Feldnamenpaaren, die durch einen Doppelpunkt voneinander getrennt sind, oder Feldnamen mit einem Doppelpunkt-Präfix. Beispiel: [<classname>][:<fieldname>]
  • Gibt dasselbe Objekt nicht zweimal aus (die Ausgabe gibt an, wann ein Objekt zuvor besucht wurde, und stellt den Hashcode für die Korrelation bereit). Dadurch werden Zirkelverweise vermieden, die Probleme verursachen

Sie können dies mit einer der beiden folgenden Methoden aufrufen:

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

Wie oben erwähnt, müssen Sie bei Stack-Überläufen darauf achten, dass Sie die Option Max Rekursion Depth verwenden, um das Risiko zu minimieren.

Hoffentlich findet jemand das nützlich!

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;
    }
}
37
John Rix

Sie können ReflectionToStringBuilder mit einem benutzerdefinierten ToStringStyle verwenden, zum Beispiel:

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

Und dann rufst du es an:

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

Hüten Sie sich jedoch vor Zirkularreferenzen!


Sie können auch json-lib ( http://json-lib.sourceforge.net ) verwenden und machen Sie einfach:

JSONObject.fromObject(value);
20
Anonymous

dadurch werden alle Felder (einschließlich Arrays von Objekten) eines Objekts gedruckt.

Fixierte Version von Ben Williams Post aus diesem Thread

Hinweis: Diese Methode verwendet die Rekursion. Wenn Sie ein sehr tiefes Objektdiagramm haben, kann es zu einem Stack-Overflow (kein Wortspiel;) kommen, wenn Sie den Parameter VM -Xss10m verwenden. Wenn Sie Eclipse verwenden, setzen Sie es in run> runconfiguration> Erweiterungen (Registerkarte) VM und klicken Sie auf "Übernehmen"

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();
}
13
user519500

Ich wollte eine elegante Lösung für dieses Problem:

  • Verwendet keine externe Bibliothek
  • Verwendet Reflection , um auf Felder einschließlich Superklassenfeldern zuzugreifen
  • Verwendet Rekursion zum Durchlaufen des Objektdiagramms mit nur einem Stapelrahmen pro Aufruf
  • Verwendet eine IdentityHashMap , um Rückwärtsreferenzen zu verarbeiten und eine unendliche Rekursion zu vermeiden
  • Behandelt Primitive, Auto-Boxing, CharSequences, Enums und Nullwerte entsprechend
  • Hier können Sie auswählen, ob statische Felder analysiert werden sollen
  • Ist einfach genug, um die Formatierungseinstellungen anzupassen

Ich habe folgende Gebrauchsklasse geschrieben:

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.<p>
     * @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<Object, Object>(), 0);
    }

    /**
     * Uses reflection and recursion to dump the contents of the given object using a custom, JSON-like notation (but not JSON).<p>
     * Parses all fields of the runtime class including super class fields, which are successively prefixed with "{@code super.}" at each level.<p>
     * {@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.<p>
     * 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.<p>
     * 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.<p>
     * 
     * @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<Object, Object>(), 0);
    }

    private static String dump(Object object, boolean isIncludingStatics, IdentityHashMap<Object, Object> 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<String, Field> fieldMap    = new TreeMap<String, Field>();  // 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<String, Field> 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 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.toString();
    }
}

Ich habe es in einer Reihe von Klassen getestet und für mich ist es äußerst effizient. Versuchen Sie es beispielsweise, um den Haupt-Thread zu sichern:

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

Bearbeiten

Seit dem Schreiben dieses Beitrags hatte ich Grund, eine iterative Version dieses Algorithmus zu erstellen. Die rekursive Version ist durch die Gesamtanzahl der Stack-Frames in der Tiefe begrenzt. Es besteht jedoch die Möglichkeit, ein extrem großes Objektdiagramm auszugeben. Um mit meiner Situation fertig zu werden, habe ich den Algorithmus überarbeitet, um anstelle des Laufzeitstapels eine Stack-Datenstruktur zu verwenden. Diese Version ist zeiteffizient und wird durch die Heap-Größe anstelle der Stack-Frametiefe begrenzt.

Sie können die iterative Version hier herunterladen und verwenden .

6
Bryan W. Wagner

Vielleicht könnten Sie dazu ein XML-Bindungsframework wie XStream , Digester oder JAXB verwenden.

4
Fabian Steeg

Sie sollten RecursiveToStringStyle verwenden:

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

Sie können Gson verwenden, um Ihr Objekt im Json-Format darzustellen:

new GsonBuilder().setPrettyPrinting().create().toJson(yourObject);
1
Ofek Ron

Ich empfehle Ihnen die Verwendung von GSON Lib für Java.

wenn Sie Maven verwenden, können Sie this verwenden.

Oder Sie können die Jar-Datei von hier herunterladen.

Hier ein Beispiel, wie man es benutzt:

Gson gson = new GsonBuilder().setPrettyPrinting().create();
String json = gson.toJson(obj);
System.out.println(json);
0
user2110287
JSONObject.fromObject(value)

Funktioniert nicht für Map-Objekte mit anderen Schlüsseln als String. Vielleicht kann JsonConfig damit umgehen.

0
Michal Moravcik