轉儲java對象的屬性

[英]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.

是否有一個庫將遞歸地轉儲/打印對象屬性?我正在尋找類似於Firebug中的console.dir()函數。

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

我知道commons-lang ReflectionToStringBuilder,但它不會遞歸到一個對象中。即。,如果我執行以下操作:

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 ]

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.

我意識到,在我的示例中,我可以重寫內部的toString()方法,但是在現實世界中,我處理的是不能修改的外部對象。

10 个解决方案

#1


41  

You could try XStream.

你可以試試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

您還可以輸出JSON。

And be careful of circular references ;)

注意循環引用;

#2


35  

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.)

我試着使用XStream作為最初的建議,但事實證明對象圖我想轉儲包括一個參考回XStream marshaller本身,它沒有仁慈(為什么它必須拋出一個異常,而不是忽視它或日志記錄一個警告,我不確定。)

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:

然后我嘗試了上面的user519500的代碼,但是我發現我需要一些調整。這里有一個類,你可以卷進一個項目,提供以下額外的特性:

  • 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>]
  • 可以忽略任何類、字段或類+字段組合的列表——只需傳遞一個包含類名、classname+fieldname對與冒號分隔的數組,或者使用冒號前綴ie的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
  • 不會兩次輸出相同的對象(輸出指示以前訪問過的對象,並提供了相關的hashcode)——這避免了引起問題的循環引用嗎?

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

#3


20  

You can use ReflectionToStringBuilder with a custom ToStringStyle, for example:

您可以使用ReflectionToStringBuilder和定制的ToStringStyle,例如:

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

And then you invoke it like:

然后你像這樣調用它:

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

Beware of circular references though!

但是要注意循環引用!


You can also use json-lib (http://json-lib.sourceforge.net) and just do:

你也可以使用json-lib (http://json-lib.sourceforge.net),只要:

JSONObject.fromObject(value);

#4


13  

this will print out all fields (including arrays of objects) of an object.

這將打印出一個對象的所有字段(包括對象數組)。

Fixed version of Ben Williams post from this thread

固定版本的本·威廉姆斯從這個帖子。

Note: this method uses recursion so If you have a very deep object graph you may get a stack-overflow (no pun intended ;) IF so you need to use the VM parameter -Xss10m. If your using eclipse put it in run>runconfiguration>augments (tab) VM augment box and press apply

注意:這個方法使用了遞歸,所以如果你有一個非常深的對象圖,你可能會得到堆棧溢出(沒有雙關語;)如果是這樣,您需要使用VM參數-Xss10m。如果您使用eclipse將其放入運行>runconfiguration>增強(tab) VM擴充框和按下應用程序。

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

#5


6  

I wanted an elegant solution to this problem that:

我想要一個優雅的解決這個問題的方法:

  • Does not use any external library
  • 不使用任何外部庫嗎?
  • Uses Reflection to access fields, including superclass fields
  • 使用反射訪問字段,包括超類字段
  • Uses recursion to traverse the Object-graph with only one stack frame per call
  • 使用遞歸遍歷對象圖,每次調用只有一個堆棧幀
  • Uses an IdentityHashMap to handle backwards references and avoid infinite recursion
  • 使用一個標識hashmap來處理反向引用並避免無限遞歸。
  • Handles primitives, auto-boxing, CharSequences, enums, and nulls appropriately
  • 適當地處理原語、自動裝箱、字符、枚舉和null
  • Allows you to choose whether or not to parse static fields
  • 允許您選擇是否解析靜態字段
  • Is simple enough to modify according to formatting preferences
  • 是否簡單到可以根據格式化首選項進行修改

I wrote the following utility class:

我寫了下面的實用類:

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

I tested it on a number of classes and for me it's extremely efficient. For example, try using it to dump the main thread:

我在很多課上測試過它,對我來說它非常有效。例如,嘗試使用它來轉儲主線程:

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

Edit

編輯

Since writing this post I had reason to create an iterative version of this algorithm. The recursive version is limited in depth by total stack frames, but you might have reason to dump an extremely large object graph. To handle my situation, I revised the algorithm to use a stack data structure in place of the runtime stack. This version is time-efficient and is limited by heap size instead of stack frame depth.

因為寫了這篇文章,我有理由創建這個算法的迭代版本。遞歸版本的深度受整個堆棧幀的限制,但是您可能有理由轉儲一個非常大的對象圖。為了處理我的情況,我修改了算法,使用堆棧數據結構代替運行時堆棧。該版本具有時間效率,並且受堆大小而不是堆棧幀深度的限制。

You can download and use the iterative version here.

您可以在這里下載並使用迭代版本。

#6


4  

You should use RecursiveToStringStyle:

您應該使用RecursiveToStringStyle:

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

#7


4  

Maybe you could use an XML binding framework like XStream, Digester or JAXB for that.

也許您可以使用XML綁定框架,比如XStream、消化器或JAXB。

#8


1  

You could use Gson to represent your object in json format :

您可以使用Gson以json格式表示對象:

new GsonBuilder().setPrettyPrinting().create().toJson(yourObject);

#9


0  

JSONObject.fromObject(value)

Does not work for Map objects with other keys than String. Maybe JsonConfig can handle this.

對於帶有除字符串之外的其他鍵的映射對象不起作用。也許JsonConfig可以處理這個問題。

#10


0  

I recommend you to use the GSON Lib fo Java.

我建議您使用GSON Java庫。

if You use Maven you can use this.

如果您使用Maven,您可以使用它。

Or you can download the Jar file from here.

或者您可以從這里下載Jar文件。

Here example how to use it:

這里的例子如何使用它:

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

注意!

本站翻译的文章,版权归属于本站,未经许可禁止转摘,转摘请注明本文地址:https://www.itdaan.com/blog/2009/03/02/729821fd83c2a5dc1e752c337b140e25.html



 
粤ICP备14056181号  © 2014-2020 ITdaan.com