56

I'm looking for a class that can output an object and all its leaf values in a format similar to this:

User
  - Name: Gordon
  - Age : 60
  - WorkAddress
     - Street: 10 Downing Street
     - Town: London
     - Country: UK
  - HomeAddresses[0]
    ...
  - HomeAddresses[1]
    ...

(Or a clearer format). This would be equivalent to:

public class User
{
    public string Name { get;set; }
    public int Age { get;set; }
    public Address WorkAddress { get;set; }
    public List<Address> HomeAddresses { get;set; }
}

public class Address
{
    public string Street { get;set; }
    public string Town { get;set; }
    public string Country { get;set; }
}

A kind of string representation of the PropertyGrid control, minus having to implement a large set of designers for each type.

PHP has something that does this called var_dump. I don't want to use a watch, as this is for printing out.

Could anyone point me to something like this if it exists? Or, write one for a bounty.

Bassie
  • 9,529
  • 8
  • 68
  • 159
Chris S
  • 64,770
  • 52
  • 221
  • 239
  • 2
    possible duplicate of [What is the best way to dump entire objects to a log in C#?](http://stackoverflow.com/questions/360277/what-is-the-best-way-to-dump-entire-objects-to-a-log-in-c) – nawfal Apr 25 '13 at 03:51

12 Answers12

55

The object dumper posted in sgmoore's link:

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

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

// See the ReadMe.html for additional information
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);
                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;
                        if (t.IsValueType || t == typeof(string)) {
                            WriteValue(f != null ? f.GetValue(element) : p.GetValue(element, null));
                        }
                        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;
                            if (!(t.IsValueType || t == typeof(string))) {
                                object value = f != null ? f.GetValue(element) : p.GetValue(element, null);
                                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("{ }");
        }
    }
}

2015 Update

YAML also serves this purpose quite well, this is how it can be done with YamlDotNet

install-package YamlDotNet

    private static void DumpAsYaml(object o)
    {
        var stringBuilder = new StringBuilder();
        var serializer = new Serializer();
        serializer.Serialize(new IndentedTextWriter(new StringWriter(stringBuilder)), o);
        Console.WriteLine(stringBuilder);
    }
Chris S
  • 64,770
  • 52
  • 221
  • 239
  • 3
    It would be awesome if there was a way to deserialize this back into an object. :) – ashes999 Jun 04 '12 at 15:08
  • 4
    `ObjectDumper` does not display contents of arrays. – Konrad Morawski Sep 20 '12 at 06:56
  • In `DumpAsYaml()` I'd rather just change `Console.WriteLine()` to `Debug.WriteLine()` so that it works not only on WebForms/Console (and probably WTF, I'm unsure) but WebForms too. Of couse I'm assuming it's debug mode (but who will call even `DumpAsYaml()` in realse mode)? – Jack Feb 21 '16 at 21:14
  • Upvote for YAML, looks like the format OP wants is pretty much YAML. Serializer is all he wants. – nawfal Mar 24 '16 at 08:33
  • //Copyright (C) Microsoft Corporation. All rights reserved. – Derek Tomes Sep 30 '16 at 01:36
  • The dumper is not handling `log4net.Util.SystemStringFormat`. Or logging with format string not handled. `log.InfoFormat()` – Purushoth Jul 17 '18 at 09:04
31

You could use the JSON serialiser, which should be easy to read for anyone use to working with JSON

User theUser = new User();
theUser.Name = "Joe";
System.Runtime.Serialization.Json.DataContractJsonSerializer serializer = new System.Runtime.Serialization.Json.DataContractJsonSerializer(myPerson.GetType());
MemoryStream ms = new MemoryStream();
serializer.WriteObject(ms, theUser );
string json = Encoding.Default.GetString(ms.ToArray()); 
Vdex
  • 1,080
  • 1
  • 10
  • 18
  • 3
    I have code that will make the JSON output more easy to read (with line feeds) here http://stackoverflow.com/questions/5881204/how-to-set-formatting-with-javascriptserializer-when-json-serializing – Mark Lakata Nov 28 '12 at 20:30
  • 1
    This is be best answer. Why not using **Serialization** to dump an object? You can simply use a JSON viewer for indentation, lists, etc. – Saeed Neamati Nov 29 '13 at 11:38
  • 5
    Also a good alternative should be to use Newtonsoft Json library, which many of us will already know and which is really easy to use. `JsonConvert.SerializeObject(items, Formatting.Indented)` for formatted output. On nuget: http://www.nuget.org/packages/Newtonsoft.Json – Ferran Salguero May 22 '14 at 14:05
  • To use a JSON serializer is an excellent tip, and using NewtonSoft JSON to do it is even better. Thanks a lot! – Michael A. Volz aka Flynn Jun 26 '14 at 07:33
  • I use your code, but not fully work, then I change to be like this: `NameValueCollection nvc = Context.Request.Form; JavaScriptSerializer serializer = new JavaScriptSerializer (); string json = serializer.Serialize (nvc); Context.Response.Write (json);` – 735 Apr 10 '15 at 06:35
14

Updated 2019

You can find the ObjectDumper project on GitHub. You can also add it via Visual Studio via NuGet package manager.

Luke Hammer
  • 2,076
  • 3
  • 18
  • 31
Dan Diplo
  • 25,076
  • 4
  • 67
  • 89
12

If you're working with markup, System.Web.ObjectInfo.Print (ASP.NET Web Pages 2) will accomplish this, nicely formatted for HTML.

For example:

@ObjectInfo.Print(new {
    Foo = "Hello",
    Bar = "World",
    Qux = new {
        Number = 42,
    },
})

In a webpage, produces:

ObjectInfo.Print(...)

Dan Lugg
  • 20,192
  • 19
  • 110
  • 174
12

Here's a visual studio extension I wrote to do this:

https://visualstudiogallery.msdn.microsoft.com/c6a21c68-f815-4895-999f-cd0885d8774f

in action: object exporter in action

Omar Elabd
  • 1,914
  • 1
  • 18
  • 23
  • 3
    Would be cool if the C# object generation stuff would be a separate dll/nuget-package so we could use it in LinqPad for on-the-fly object-to-code dumps. After nearly a decade as .net dev I still miss the perl `Data::Dumper`. – mbx Aug 27 '15 at 15:49
  • 4
    This is a great extension Thanks. – Waleed A.K. Oct 13 '15 at 17:43
  • 1
    This looks like it's meant to be used during interactive debugging -- is that right? Or can this be used in other ways? – Dan Esparza Dec 06 '15 at 01:49
  • 1
    @DanEsparza that is correct, you need to be stopped at a breakpoint to export the object. Similar to how you would use the locals or watch window. – Omar Elabd Dec 07 '15 at 21:26
  • @beppe9000 no since this relies on the Visual Studio Environment to retrieve values not reflection. You can already serialize to XML or JSON format quite easily in code through popular libraries. Are you trying to specifically serialize an object to a C# POCO? – Omar Elabd Nov 01 '16 at 15:34
9

I know this is an old question, but thought I'd throw out an alternative that worked for me, took me about two minutes to do.

Install Newtonsoft Json.NET: http://james.newtonking.com/json

(or nuget version) http://www.nuget.org/packages/newtonsoft.json/

Reference Assembly:

using Newtonsoft.Json;

Dump JSON string to log:

txtResult.Text = JsonConvert.SerializeObject(testObj);
Nathan Pond
  • 336
  • 3
  • 5
  • The only bad thing about this solution is enums show up in the numeric form instead of their text value. A nice solution though otherwise. – Simon The Cat Mar 28 '14 at 14:31
  • 1
    Here's a one liner with pretty formatting and enum names: `Newtonsoft.Json.JsonConvert.SerializeObject(testObj, Newtonsoft.Json.Formatting.Indented, new Newtonsoft.Json.JsonSerializerSettings() { Converters = new List { new Newtonsoft.Json.Converters.StringEnumConverter() } })` – Dejan Oct 11 '17 at 13:09
  • And if you want to serialize the private fields to, you can annotate the corresponding class(es) with: `[JsonObject(MemberSerialization.Fields)]` – Dejan Oct 11 '17 at 13:18
7

You could write that very easily with a little bit of reflection. Something kind of like:

public void Print(object value, int depth)
{
    foreach(var property in value.GetType().GetProperties())
    {
        var subValue = property.GetValue(value);
        if(subValue is IEnumerable)
        {
             PrintArray(property, (IEnumerable)subValue);
        }
        else
        {
             PrintProperty(property, subValue);
        }         
    }
}

You can write up the PrintArray and PrintProperty methods.

Jake Pearson
  • 27,069
  • 12
  • 75
  • 95
  • 6
    Closest I have seen is ObjectDumper. See this question http://stackoverflow.com/questions/360277/what-is-the-best-way-to-dump-entire-objects-to-a-log-in-c – sgmoore Aug 28 '09 at 14:49
  • 1
    I had written mostly what Jakers had but thought this must exist. I'll give jakers 1 for the effort (it doesn't compile but that's not a problem). Looks like sgmoore's link makes this closeable – Chris S Aug 28 '09 at 14:56
  • I get a `No overload for method 'GetValue' takes 1 arguments` with this.. – Bassie May 03 '17 at 10:44
7

I have a handy T.Dump() Extension method that should be pretty close to the results you're looking for. As its an extension method, its non-invasive and should work on all POCO objects.

Example Usage

var model = new TestModel();
Console.WriteLine(model.Dump());

Example Output

{
    Int: 1,
    String: One,
    DateTime: 2010-04-11,
    Guid: c050437f6fcd46be9b2d0806a0860b3e,
    EmptyIntList: [],
    IntList:
    [
        1,
        2,
        3
    ],
    StringList:
    [
        one,
        two,
        three
    ],
    StringIntMap:
    {
        a: 1,
        b: 2,
        c: 3
    }
}
mythz
  • 141,670
  • 29
  • 246
  • 390
2

If you don't feel like copying and pasting Chris S's code, the Visual Studio 2008 samples come with an ObjectDumper.

Drive:\Program Files\Microsoft Visual Studio 9.0\Samples\1033\LinqSamples\ObjectDumper

AJ.
  • 16,368
  • 20
  • 95
  • 150
2

Here is an alternative:

using System.Reflection;
public void Print(object value)
{
    PropertyInfo[] myPropertyInfo;
    string temp="Properties of "+value+" are:\n";
    myPropertyInfo = value.GetType().GetProperties();
    for (int i = 0; i < myPropertyInfo.Length; i++)
    {
        temp+=myPropertyInfo[i].ToString().PadRight(50)+" = "+myPropertyInfo[i].GetValue(value, null)+"\n";
    }
    MessageBox.Show(temp);
}

(just touching level 1, no depth, but says a lot)

e-motiv
  • 5,795
  • 5
  • 27
  • 28
  • Please make this seem more like an alternative answer, received your flag and read it again. I removed it because (initially) it looked like a follow up comment. – Tim Post Nov 08 '11 at 12:56
  • I don't know really why a follow up comment is different or should be deleted, but I'll try to make it look like alternative answer. – e-motiv Nov 19 '11 at 13:38
  • Answers should directly answer the question, not just bring more to the conversation. Comments (under each answer) will be available to you once you hit 50 reputation points. This does, however, speak directly to the question, Thanks in advance for editing it for a little clarity – Tim Post Nov 19 '11 at 14:03
1

For most classes, you could use the DataContractSerializer

Ohad Schneider
  • 36,600
  • 15
  • 168
  • 198
1

I just came across a similar requirement in a Blazor project, and came up with the following very simple component to output an object's (and it's child objects') data to the screen:

ObjectDumper.razor:

@using Microsoft.AspNetCore.Components
@using Newtonsoft.Json

  <div>
    <button onclick="@DumpVMToConsole">@ButtonText</button>
    <pre id="json">@_objectAsJson</pre>
  </div>


@functions {

  // This component allows the easy visualisation of the values currently held in 
  // an object and its child objects.  Add this component to a page and pass in a 
  // param for the object to monitor, then press the button to see the object's data
  // as nicely formatted JSON
  // Use like this:  <ObjectDumper ObjectToDump="@_billOfLadingVM" />

  [Parameter]
  private object ObjectToDump { get; set; }

  [Parameter]
  private string ButtonText { get; set; } = "Show object's data";

  string _buttonText;

  string _objectAsJson = "";

  public void DumpVMToConsole()
  {
    _objectAsJson = GetObjectAsFormattedJson(ObjectToDump);
    Console.WriteLine(_objectAsJson);
  }

  public string GetObjectAsFormattedJson(object obj)
  {
    return JsonConvert.SerializeObject(
      value: obj, 
      formatting: Formatting.Indented, 
      settings: new JsonSerializerSettings
      {
        PreserveReferencesHandling = PreserveReferencesHandling.Objects
      });
  }

}

You then stick that somewhere on a Blazor page as follows:

<ObjectDumper ObjectToDump="@YourObjectToVisualise" />

Which then renders a button you can press to see the current values of the bound object:

enter image description here

I've stuck that in a GitHub repo: tomRedox/BlazorObjectDumper

tomRedox
  • 28,092
  • 24
  • 117
  • 154