转储java对象的属性

时间:2009-03-02 16:38:01

标签: java reflection properties dump tostring

是否存在递归转储/打印对象属性的库?我正在寻找类似于Firebug中console.dir()函数的东西。

我知道公共语言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";
    }
}

我收到:

  

的ToString $外@ 1b67f74 [   的intValue = 5
  innerValue = $的ToString内蒙古@ 530daa   ]

我意识到在我的例子中,我可以覆盖Inner的toString()方法,但在现实世界中,我正在处理我无法修改的外部对象。

10 个答案:

答案 0 :(得分:41)

您可以尝试XStream

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

打印出来:

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

您也可以在JSON

中输出

小心循环引用;)

答案 1 :(得分:38)

我尝试使用最初建议的XStream,但事实证明我想要转储的对象图包含一个引用回XStream marshaller本身,它并没有太过善意(为什么它必须抛出异常而不是忽略它或记录一个好警告,我不确定。)

然后我尝试了上面的user519500的代码,但发现我需要一些调整。这是一个可以滚动到项目中的类,它提供以下额外功能:

  • 可以控制最大递归深度
  • 可以限制数组元素输出
  • 可以忽略任何类,字段或类+字段组合的列表 - 只需传递一个数组,其中包含任何类名组合,用冒号分隔的classname + fieldname对,或带冒号前缀的字段名,即:{{1} }
  • 不会输出相同的对象两次(输出指示先前访问过对象并提供相关的哈希码) - 这可以避免导致问题的循环引用

您可以使用以下两种方法之一来调用它:

[<classname>][:<fieldname>]

如上所述,您需要注意堆栈溢出,因此请使用最大递归深度工具来降低风险。

希望有人会觉得这很有用!

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

答案 2 :(得分:20)

您可以将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());
    }
}

然后你调用它:

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

请注意循环引用!


您也可以使用json-lib(http://json-lib.sourceforge.net)并执行:

JSONObject.fromObject(value);

答案 3 :(得分:13)

这将打印出对象的所有字段(包括对象数组)。

来自this thread

的Ben Williams帖子的修正版

注意:此方法使用递归,因此如果您有一个非常深的对象图,您可能会得到堆栈溢出(没有双关语;)IF因此您需要使用VM参数-Xss10m。如果您使用eclipse将其置于运行&gt;运行配置&gt;扩充(选项卡)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();
}

答案 4 :(得分:6)

我想要一个优雅的解决方案来解决这个问题:

  • 不使用任何外部库
  • 使用Reflection访问字段,包括超类字段
  • 使用递归来遍历每次调用只有一个堆栈帧的对象图
  • 使用IdentityHashMap处理向后引用并避免无限递归
  • 适当处理原语,自动装箱,CharSequences,枚举和空值
  • 允许您选择是否解析静态字段
  • 足够简单,可根据格式偏好进行修改

我编写了以下实用程序类:

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

我在很多课上测试过,对我来说这非常有效。例如,尝试使用它来转储主线程:

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

修改

自写这篇文章以来,我有理由创建这个算法的迭代版本。递归版本的深度受到总堆栈帧的限制,但您可能有理由转储极大的对象图。为了处理我的情况,我修改了算法以使用堆栈数据结构代替运行时堆栈。此版本具有时间效率,受堆大小限制,而不受堆栈帧深度的限制。

您可以下载并使用iterative version here

答案 5 :(得分:4)

也许您可以使用XStreamDigesterJAXB这样的XML绑定框架。

答案 6 :(得分:4)

你应该使用RecursiveToStringStyle:

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

答案 7 :(得分:1)

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

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

答案 8 :(得分:0)

JSONObject.fromObject(value)

对于具有除String之外的其他键的Map对象不起作用。也许JsonConfig可以解决这个问题。

答案 9 :(得分:0)

我建议您使用GSON Lib for Java。

如果您使用Maven,则可以使用this

或者您可以从here下载Jar文件。

此处示例如何使用它:

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