是否存在递归转储/打印对象属性的库?我正在寻找类似于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()方法,但在现实世界中,我正在处理我无法修改的外部对象。
答案 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>]
如上所述,您需要注意堆栈溢出,因此请使用最大递归深度工具来降低风险。
希望有人会觉得这很有用!
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)
这将打印出对象的所有字段(包括对象数组)。
的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)
我想要一个优雅的解决方案来解决这个问题:
我编写了以下实用程序类:
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)
答案 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)