KBD

Keith Devens .com

Friday, July 25, 2008 Flag waving
And we know that in all things God works for the good of those who love him, who have been... – Paul (Romans 8:28)
← I SO want a Murky pet!Apple - Boot Camp →

Daily link icon Tuesday, April 4, 2006

Pretty-print .NET objects?

Does anyone know of a way to pretty-print .NET objects? We've covered before how .NET objects don't stringify into anything good, so I'm looking for a pretty-printer to aid in debugging. .NET has such good reflection that I'm surprised I can't find a pre-existing library or built-in .NET facility. Do I have to take a half hour and write my own pretty-printer?

Update (2:29pm): There we go, this only took me about 20 minutes:

using System;
using System.Text;
using System.Reflection;

class foobar{
    string foo, bar, baz;
    bar b = new bar();
}
class bar{
    string roar, blah;
}

namespace PrettyPrinter{
    public class PrettyPrinter{
        public static void Main(string[] args){
            foobar f = new foobar();
            Console.WriteLine(pp(f));
            int i = 2;
            Console.WriteLine(pp(i));
        }
        public static string pp(object o){
            StringBuilder sb = new StringBuilder();
            pp(sb, o, 0);
            return sb.ToString();
        }
        public static void pp(StringBuilder sb, object o, int level){
            Type t = o.GetType();
            FieldInfo[] members = t.GetFields(
                        BindingFlags.Public
                        | BindingFlags.NonPublic
                        | BindingFlags.Instance
            );

            foreach(FieldInfo info in members){
                object val = info.GetValue(o);
                sb.Append('\t',level);
                sb.Append(info.ToString()).Append(": ");
                if(val == null){
                    sb.AppendLine("null");
                }else if(info.FieldType.IsClass){
                    sb.AppendLine("{");
                    pp(sb, val, level+1);
                    sb.Append('\t',level).AppendLine("}");
                }else{
                    sb.AppendLine(o.ToString());
                }
            }
        }
    }
}

Output:

C:\DEV\Projects\test\bin\Debug>test
System.String foo: null
System.String bar: null
System.String baz: null
bar b: {
        System.String roar: null
        System.String blah: null
}

Int32 m_value: 2

Update (4:57pm): Since of course that was a first pass, here's a more correct version that handles circular references, and for Keith also dumps XML if you happen to pass it an XmlNode Smiley

using System;
using System.Xml;
using System.Text;
using System.Reflection;
using System.Collections.Generic;
using pp = PrettyPrinter.PrettyPrinter;

namespace PrettyPrinter{
    public class PrettyPrinter{
        private static Type str = typeof(string);
        private static Type xml = typeof(XmlNode);

        public static string pp(object o){
            if(o == null)
                return "null";

            StringBuilder sb = new StringBuilder();
            sb.Append(o.GetType()).Append(": ");
            pp(new Dictionary<object, bool>(), sb, o, 0);
            return sb.ToString();
        }

        public static void pp(
                Dictionary<object, bool> seen_objects,
                StringBuilder sb, object o, int level
        ){
            Type t = o.GetType();
            if(t == str || t.IsValueType){
                sb.AppendLine(o.ToString());
            }else if(xml.IsInstanceOfType(o)){
                sb.AppendLine(((XmlNode)o).OuterXml);
            }else if(t.IsClass){
                if(seen_objects.ContainsKey(o)){
                    sb.AppendLine("RECURSION!");
                    return;
                }
                seen_objects.Add(o, true);
                FieldInfo[] fields = t.GetFields(
                    BindingFlags.Public
                    | BindingFlags.NonPublic
                    | BindingFlags.Instance
                );
                sb.AppendLine("{");
                foreach(FieldInfo info in fields){
                    object val = info.GetValue(o);
                    sb.Append('\t',level+1).Append(info).Append(": ");
                    if(val == null){
                        sb.AppendLine("null");
                    }else{
                        pp(seen_objects, sb, val, level+1);
                    }
                }
                sb.Append('\t',level).AppendLine("}");
            }
        }
    }
}

class foobar{
    string foo, bar, baz = "roar";
    public testing.bar b = new testing.bar();
}

namespace testing{
    class bar{
        string roar, blah;
        public foobar f;
    }
    class baz{
        public baz b = null;
    }
}

public class Test{
    public static void Main(string[] args){
        foobar f = new foobar();
        f.b.f = f;
        Console.WriteLine(pp.pp(f));

        int i = 2;
        Console.WriteLine(pp.pp(i));

        XmlDocument doc = new XmlDocument();
        doc.LoadXml(@"<?xml version=""1.0""?>
            <foo><bar><baz>roar</baz></bar></foo>"
        );
        Console.WriteLine(pp.pp(doc.ChildNodes[1]));

        testing.baz b = new testing.baz();
        b.b = b;
        Console.WriteLine(pp.pp(b));
    }
}

Output:

C:\DEV\Projects\test\bin\Debug>test
foobar: {
        System.String foo: null
        System.String bar: null
        System.String baz: roar
        testing.bar b: {
                System.String roar: null
                System.String blah: null
                foobar f: RECURSION!
        }
}

System.Int32: 2

System.Xml.XmlElement: <foo><bar><baz>roar</baz></bar></foo>

testing.baz: {
        testing.baz b: RECURSION!
}

(code reformatted to not break my layout)

← I SO want a Murky pet!Apple - Boot Camp →

Comments XML gif

Keith Gaughan (http://talideon.com/) wrote:

Don't forget to check the object graph for loops.

∴ Keith Gaughan | 4-Apr-2006 2:47pm est | http://talideon.com/ | #9376

Feel free to post a comment below. Please see my comment policy.

Formatting Rules (No HTML):

  • **bold**, *italic*, _underlined_, --strikeout--
  • "text"="url" creates a link, and URLs are auto-highlighted
  • Blockquote: Like e-mail, begin paragraph with > (greater-than sign)
  • Lists: begin paragraph with *,-, or + (unordered), or # (ordered)
  • Code block: ?!code:language=perl|php|sql|javascript|etc.{\n}...{\n}?!/code

:
(will be your IP address if blank)
: (optional)
(Will not be shown on site)

: (optional)
:

July 2008
SunMonTueWedThuFriSat
 12345
6789101112
13141516171819
20212223242526
2728293031 



RSS feed RSS feed for Keith's Weblog
Atom feed Atom feed for Keith's Weblog
Weblog archive

Generated in about 0.306s.

(Used 8 db queries)

mobile phone