ObjectDumper LINQ To Export Collection Via .NET Reflection

By Peter Bromberg

ObjectDumper dumps out a collection to the console or to a file via reflection

When developing C# programs in the functional programming style, or with LINQ, you often need to dump out a collection to the console or to a file. Object dumper is a great tool to use for this. It is a sample from the C# samples that install with Visual Studio 2008 and usually can be found at C:\Program Files\Microsoft Visual Studio 9.0\Samples\1033\CSharpSamples.zip. It also comes with the Visual Studio 2010 Samples.

ObjectDumper, as it is currently written, has some bugs. It cannot get the values of objects like NumberFormatInfo without throwing an exception and stopping dead in it's tracks, so I've made some modifications to the class in order to allow it to continue without "blowing up". Here is my revised ObjectDumper Class:

/* USAGE:

try
{
StringBuilder sb = new StringBuilder();
StringWriter sw = new StringWriter(sb);
ObjectDumper.Write(yourObject, 5, sw);
File.WriteAllText(@"C:\temp\object.txt", sb.ToString());
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message + ex.StackTrace);
}
*/

//Copyright (C) Microsoft Corporation. All rights reserved.

using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;

namespace LinqUtils
{
public class ObjectDumper
{

public static void Write( object element )
{
Write(element, 0);
}

public static void Write( object element, int depth )
{
Write(element, depth, Console.Out);
}

public static void Write( object element, int depth, TextWriter log )
{
ObjectDumper dumper = new ObjectDumper(depth);
dumper.writer = log;
dumper.WriteObject(null, element);
}

TextWriter writer;
int pos;
int level;
int depth;

private ObjectDumper( int depth )
{
this.depth = depth;
}

private void Write( string s )
{
if (s != null)
{
writer.Write(s);
pos += s.Length;
}
}

private void WriteIndent()
{
for (int i = 0; i < level; i++) writer.Write(" ");
}

private void WriteLine()
{
writer.WriteLine();
pos = 0;
}

private void WriteTab()
{
Write(" ");
while (pos % 8 != 0) Write(" ");
}

private void WriteObject( string prefix, object element )
{
if (element == null || element is ValueType || element is string)
{
WriteIndent();
Write(prefix);
WriteValue(element);
WriteLine();
}
else
{
IEnumerable enumerableElement = element as IEnumerable;
if (enumerableElement != null)
{
foreach (object item in enumerableElement)
{
if (item is IEnumerable && !(item is string))
{
WriteIndent();
Write(prefix);
Write("...");
WriteLine();
if (level < depth)
{
level++;
WriteObject(prefix, item);
level--;
}
}
else
{
WriteObject(prefix, item);
}
}
}
else
{
MemberInfo[] members = element.GetType().GetMembers(BindingFlags.Public | BindingFlags.Instance /*| BindingFlags.NonPublic */);
Console.WriteLine(members.Length + " Members.");
WriteIndent();
Write(prefix);
bool propWritten = false;
foreach (MemberInfo m in members)
{
FieldInfo f = m as FieldInfo;
PropertyInfo p = m as PropertyInfo;
if (f != null || p != null)
{
if (propWritten)
{
WriteTab();
}
else
{
propWritten = true;
}
Write(m.Name);
Write("=");
Type t = f != null ? f.FieldType : p.PropertyType;
object o = null;
if (t.IsValueType || t == typeof(string))
{
try
{

o = f != null ? f.GetValue(element) : p.GetValue(element, null);
WriteValue(o);
}
catch (Exception ex)
{
// Had to do this because exceptions are thrown on NumberFormatInfo and other types
// at high depth levels, even though there is a legitimate value obtained.
Console.WriteLine(ex.Message);
WriteValue(o);
}
}
else
{
if (typeof(IEnumerable).IsAssignableFrom(t))
{
Write("...");
}
else
{
Write("{ }");
}
}
}
}
if (propWritten) WriteLine();
if (level < depth)
{
foreach (MemberInfo m in members)
{
FieldInfo f = m as FieldInfo;
PropertyInfo p = m as PropertyInfo;
if (f != null || p != null)
{
Type t = f != null ? f.FieldType : p.PropertyType;
object value = null;
if (!(t.IsValueType || t == typeof(string)))
{
try
{
value = f != null ? f.GetValue(element) : p.GetValue(element, null);
}
catch
{
}

if (value != null)
{
level++;
WriteObject(m.Name + ": ", value);
level--;
}
}
}
}
}
}
}
}

private void WriteValue( object o )
{
if (o == null)
{
Write("null");
}
else if (o is DateTime)
{
Write(((DateTime)o).ToShortDateString());
}
else if (o is ValueType || o is string)
{
Write(o.ToString());
}
else if (o is IEnumerable)
{
Write("...");
}
else
{
Write("{ }");
}
}
}
}

The downloadable sample test app creates a simple DataSet containing a single DataTable with two columns and one row and runs it through ObjectDumper to save to a text file:

using System.Text;
using LinqUtils;
using System.Data;
using System.IO;


namespace Test
{
class Program
{
static void Main( string[] args )
{
DataSet ds = new DataSet();
DataTable dt = new DataTable();
dt.Columns.Add("Name", typeof (string));
dt.Columns.Add("Age", typeof(int));
DataRow row = dt.NewRow();
row.ItemArray = new Object[] {"George Washington", 44};
dt.Rows.Add(row);
ds.Tables.Add(dt);
try
{
StringBuilder sb = new StringBuilder();
StringWriter sw = new StringWriter(sb);
ObjectDumper.Write(ds, 4, sw);
File.WriteAllText(@"C:\temp\object.txt", sb.ToString());
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message + ex.StackTrace);
}
}
}
}

You can experiment with this at different depth levels (I'm using 4 in the sample). Just using a 4 depth level outputs a 49K Text file for the DataSet, showing properties you'd never think were in this class. Increasing the depth level can create text files up to 16,000K! If you want to see private members, just uncomment the /*| BindingFlags.NonPublic */ -- then you'll see even more cool stuff in the output.

I have found ObjectDumper to be useful especially dealing with the results of complex LINQ queries. You can download the sample with the revised class here.


Popularity  (1629 Views)
Picture
Biography - Peter Bromberg
Peter Bromberg is a C# MVP, MCP, and .NET expert who has worked in banking, financial and telephony for over 20 years. Pete focuses exclusively on the .NET Platform, and currently develops SOA and other .NET applications for a Fortune 500 clientele. Peter enjoys producing digital photo collage with Maya,playing jazz flute, the beach, and fine wines. You can view Peter's UnBlog and IttyUrl sites. Follow Microsoft MVP
Create New Account
Article Discussion: Fun With ObjectDumper
Peter Bromberg posted at Thursday, August 26, 2010 9:24 AM