Java toString – ToStringBuilder不够用; 不会遍历

我需要能够遍历整个对象图并记录所有成员字段的所有内容。

例如:对象A具有对象B的集合,其具有对象C的集合,并且A,B,C具有其他字段,等等。

Apache Commons ToStringBuilder是不够的,因为它不会遍历对象图或输出集合的内容。

有没有人知道另一个库会做这个或有一个代码片段来做到这一点?

您可以使用org.apache.commons.lang.builder.ReflectionToStringBuilder遍历整个树。 诀窍在于,在ToStringStyle您需要遍历该值。 ToStringStyle将处理已经处理的值,并且不允许递归。 开始了:

 System.out.println(ReflectionToStringBuilder.toString(schema, new RecursiveToStringStyle(5))); private static class RecursiveToStringStyle extends ToStringStyle { private static final int INFINITE_DEPTH = -1; /** * Setting {@link #maxDepth} to 0 will have the same effect as using original {@link #ToStringStyle}: it will * print all 1st level values without traversing into them. Setting to 1 will traverse up to 2nd level and so * on. */ private int maxDepth; private int depth; public RecursiveToStringStyle() { this(INFINITE_DEPTH); } public RecursiveToStringStyle(int maxDepth) { setUseShortClassName(true); setUseIdentityHashCode(false); this.maxDepth = maxDepth; } @Override protected void appendDetail(StringBuffer buffer, String fieldName, Object value) { if (value.getClass().getName().startsWith("java.lang.") || (maxDepth != INFINITE_DEPTH && depth >= maxDepth)) { buffer.append(value); } else { depth++; buffer.append(ReflectionToStringBuilder.toString(value, this)); depth--; } } // another helpful method @Override protected void appendDetail(StringBuffer buffer, String fieldName, Collection coll) { depth++; buffer.append(ReflectionToStringBuilder.toString(coll.toArray(), this, true, true)); depth--; } } 

这是@dma_k解决方案的修改版本,具有单缓冲区重用,线程安全性,多行缩进以及对象的toString方法(如果已被覆盖)的使用。

样本输出:

 ToStringTest.ParentStub { array = {a,b,c} map = {key2=null, key1=value1} child = ToStringTest.Stub { field1 = 12345 field2 = Hello superField = abc } empty =  superField = abc } 

码:

 class RecursiveToStringStyle extends ToStringStyle { private static final RecursiveToStringStyle INSTANCE = new RecursiveToStringStyle(13); public static ToStringStyle getInstance() { return INSTANCE; } public static String toString(Object value) { final StringBuffer sb = new StringBuffer(512); INSTANCE.appendDetail(sb, null, value); return sb.toString(); } private final int maxDepth; private final String tabs; // http://stackoverflow.com/a/16934373/603516 private ThreadLocal depth = new ThreadLocal() { @Override protected MutableInteger initialValue() { return new MutableInteger(0); } }; protected RecursiveToStringStyle(int maxDepth) { this.maxDepth = maxDepth; tabs = StringUtils.repeat("\t", maxDepth); setUseShortClassName(true); setUseIdentityHashCode(false); setContentStart(" {"); setFieldSeparator(SystemUtils.LINE_SEPARATOR); setFieldSeparatorAtStart(true); setFieldNameValueSeparator(" = "); setContentEnd("}"); } private int getDepth() { return depth.get().get(); } private void padDepth(StringBuffer buffer) { buffer.append(tabs, 0, getDepth()); } private StringBuffer appendTabified(StringBuffer buffer, String value) { //return buffer.append(String.valueOf(value).replace("\n", "\n" + tabs.substring(0, getDepth()))); Matcher matcher = Pattern.compile("\n").matcher(value); String replacement = "\n" + tabs.substring(0, getDepth()); while (matcher.find()) { matcher.appendReplacement(buffer, replacement); } matcher.appendTail(buffer); return buffer; } @Override protected void appendFieldSeparator(StringBuffer buffer) { buffer.append(getFieldSeparator()); padDepth(buffer); } @Override public void appendStart(StringBuffer buffer, Object object) { depth.get().increment(); super.appendStart(buffer, object); } @Override public void appendEnd(StringBuffer buffer, Object object) { super.appendEnd(buffer, object); buffer.setLength(buffer.length() - getContentEnd().length()); buffer.append(SystemUtils.LINE_SEPARATOR); depth.get().decrement(); padDepth(buffer); appendContentEnd(buffer); } @Override protected void removeLastFieldSeparator(StringBuffer buffer) { int len = buffer.length(); int sepLen = getFieldSeparator().length() + getDepth(); if (len > 0 && sepLen > 0 && len >= sepLen) { buffer.setLength(len - sepLen); } } private boolean noReflectionNeeded(Object value) { try { return value != null && (value.getClass().getName().startsWith("java.lang.") || value.getClass().getMethod("toString").getDeclaringClass() != Object.class); } catch (NoSuchMethodException e) { throw new IllegalStateException(e); } } @Override protected void appendDetail(StringBuffer buffer, String fieldName, Object value) { if (getDepth() >= maxDepth || noReflectionNeeded(value)) { appendTabified(buffer, String.valueOf(value)); } else { new ReflectionToStringBuilder(value, this, buffer, null, false, false).toString(); } } // another helpful method, for collections: @Override protected void appendDetail(StringBuffer buffer, String fieldName, Collection coll) { buffer.append(ReflectionToStringBuilder.toString(coll.toArray(), this, true, true)); } static class MutableInteger { private int value; MutableInteger(int value) { this.value = value; } public final int get() { return value; } public final void increment() { ++value; } public final void decrement() { --value; } } } 

我不知道一个库,但用reflectionapi和一些递归很容易:

 printMembers(Object instance) foreach field if (field is primitive or String) // guess you're interested in the String value printPrimitive(field) else if (field is array or collection) foreach item in field printmembers(item) else printmembers(field) // no primitve, no array, no collection -> object 

获取所有字段对Java Reflection API来说不是问题。 如果字段是数组或Iterable的实例,只需使用迭代器来获取所有数组/集合处理程序。

使用自定义实现,您可以自由地为特殊对象添加特殊处理程序(例如将String视为基元)以避免日志中的混乱。

这是我为个人使用而写的东西。 如果有帮助请告诉我:

 public static String arrayToString(final Object obj){ if (obj == null) { return ""; } else { Object array = null; if (obj instanceof Collection) { array = ((Collection) obj).toArray(); } else if (obj.getClass().isArray()) { array = obj; } else { return notNull(obj); } int length = Array.getLength(array); int lastItem = length - 1; StringBuffer sb = new StringBuffer("["); for (int i = 0; i < length; i++) { sb.append(arrayToString(Array.get(array, i))); if (i < lastItem) { sb.append(", "); } } sb.append(']'); return sb.toString(); } } 

这个链接最终成为一个很好的起点。 你基本上需要一些递归的东西,但不会在循环引用中丢失(对象A有对象B的引用,它引用了对象A;你不想被一遍又一遍地遍历)。

http://www.java2s.com/Code/Java/Class/Constructsprettystringrepresentationofobjectvalue.htm

这也有点帮助

http://binkley.blogspot.com/2007/08/recursive-tostring.html

扩展了List和Map的上述代码:

代码:

 public class MultipleRecursiveToStringStyle extends ToStringStyle { private static final int INFINITE_DEPTH = -1; private int maxDepth; private int depth; public MultipleRecursiveToStringStyle() { this(INFINITE_DEPTH); } public MultipleRecursiveToStringStyle(int maxDepth) { setUseShortClassName(true); setUseIdentityHashCode(false); this.maxDepth = maxDepth; } @Override protected void appendDetail(StringBuffer buffer, String fieldName, Object value) { if (value.getClass().getName().startsWith("java.lang.") || (maxDepth != INFINITE_DEPTH && depth >= maxDepth)) { buffer.append(value); } else { depth++; buffer.append(ReflectionToStringBuilder.toString(value, this)); depth--; } } @Override protected void appendDetail(StringBuffer buffer, String fieldName, Collection coll) { Collections.sort((List) coll); for(Object value: coll){ if (value.getClass().getName().startsWith("java.lang.") || (maxDepth != INFINITE_DEPTH && depth >= maxDepth)) { buffer.append(value); } else { depth++; buffer.append(ReflectionToStringBuilder.toString(value, this)); depth--; } } } @Override protected void appendDetail(StringBuffer buffer, String fieldName, Map map) { TreeMap sortedMap = new TreeMap(map); for(Map.Entry kvEntry: sortedMap.entrySet()){ Object value = kvEntry.getKey(); if (value.getClass().getName().startsWith("java.lang.") || (maxDepth != INFINITE_DEPTH && depth >= maxDepth)) { buffer.append(value); } else { depth++; buffer.append(ReflectionToStringBuilder.toString(value, this)); depth--; } value = kvEntry.getValue(); if (value.getClass().getName().startsWith("java.lang.") || (maxDepth != INFINITE_DEPTH && depth >= maxDepth)) { buffer.append(value); } else { depth++; buffer.append(ReflectionToStringBuilder.toString(value, this)); depth--; } } }}