188

So for viewing a current object's state at runtime, I really like what the Visual Studio Immediate window gives me. Just doing a simple

? objectname

Will give me a nicely formatted 'dump' of the object.

Is there an easy way to do this in code, so I can do something similar when logging?

Matas Vaitkevicius
  • 58,075
  • 31
  • 238
  • 265
Dan Esparza
  • 28,047
  • 29
  • 99
  • 127
  • In the end, I've used T.Dump quite a bit. It's a pretty solid solution -- you just need to be careful of recursion. – Dan Esparza Dec 01 '15 at 13:23
  • This is an old question, but comes out at the top of a lot of search hits. For future readers: See [this vs extension](http://stackoverflow.com/a/29872430/906773). Worked great for me in VS2015. – Jesse Good Dec 06 '15 at 01:20
  • 1
    Update for 2020 since that VS plugin isn't maintained and lacks some features. The following library does the same thing in code - and it has a few extra features, e.g. it tracks where it's already visited to avoid loops: https://github.com/thomasgalliker/ObjectDumper – Nick Westgate Mar 17 '20 at 04:17

17 Answers17

100

For a larger object graph, I second the use of Json but with a slightly different strategy. First I have a static class that is easy to call and with a static method that wraps the Json conversion (note: could make this an extension method).

using Newtonsoft.Json;

public static class F
{
    public static string Dump(object obj)
    {
        return JsonConvert.SerializeObject(obj);
    }
}

Then in your Immediate Window,

var lookHere = F.Dump(myobj);

lookHere will auto-show up in the Locals window prepended with a $ or you can add a watch to it. On the right hand side of the Value column in the inspector, there is a magnifying glass with a dropdown caret beside it. Choose the dropdown caret and choose Json visualizer.

Screenshot of Visual Studio 2013 Locals window

I am using Visual Studio 2013.

Jason
  • 4,897
  • 2
  • 33
  • 40
62

You could base something on the ObjectDumper code that ships with the Linq samples.
Have also a look at the answer of this related question to get a sample.

Community
  • 1
  • 1
Mike Scott
  • 12,274
  • 8
  • 40
  • 53
  • 5
    It also doesn't work for arrays (it just displays the type and the length of the array, but doesn't print its contents). – Konrad Morawski Sep 20 '12 at 06:57
  • 6
    [nuget package for ObjectDumper](http://www.nuget.org/packages/ObjectDumper/) is now available. It also provides an extension method `DumpToString` and `Dump` to `Object` class. Handy. – IsmailS Jun 17 '15 at 09:43
  • 2
    `w3wp.exe` crashes when I attempt to use `ObjectDumper` like `Request.DumpToString("aaa");` – Paul May 02 '16 at 15:39
43

You could use Visual Studio Immediate Window

Just paste this (change actual to your object name obviously):

Newtonsoft.Json.JsonConvert.SerializeObject(actual);

It should print object in JSON enter image description here

You should be able to copy it over textmechanic text tool or notepad++ and replace escaped quotes (\") with " and newlines (\r\n) with empty space, then remove double quotes (") from beginning and end and paste it to jsbeautifier to make it more readable.

UPDATE to OP's comment

public static class Dumper
{
    public static void Dump(this object obj)
    {
        Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(obj)); // your logger
    }
}

this should allow you to dump any object.

Hope this saves you some time.

Matas Vaitkevicius
  • 58,075
  • 31
  • 238
  • 265
  • Thanks. Perhaps you didn't catch it in my original question, but I indicated that I already knew about the immediate window, and I wanted to do the same thing when logging in my app. – Dan Esparza Jun 23 '15 at 15:38
  • @DanEsparza `Console.Log(Newtonsoft.Json.JsonConvert.SerializeObject(actual));` ? :) and yes I indeed missed it. This question comes up when you search https://www.google.co.uk/search?num=100&safe=off&espv=2&q=c%23+object+to+json+immediate+window&oq=c%23+object+to+json+immediate+wind&gs_l=serp.3.0.33i21.3714.5236.0.6083.9.8.1.0.0.0.302.860.0j2j1j1.4.0.ckpsrh...0...1.1.64.serp..7.2.492.S1GYb6M5xFw – Matas Vaitkevicius Jun 23 '15 at 15:52
  • 2
    FYI, when you have a JSON string in a C# string, click on the spyglass icon to the right of the string and pick the Text Visualizer. It will bring up a window that shows a plain text version of the JSON string (not escaped quotes or \r\n). – Walter Aug 31 '16 at 01:00
29

I'm certain there are better ways of doing this, but I have in the past used a method something like the following to serialize an object into a string that I can log:

  private string ObjectToXml(object output)
  {
     string objectAsXmlString;

     System.Xml.Serialization.XmlSerializer xs = new System.Xml.Serialization.XmlSerializer(output.GetType());
     using (System.IO.StringWriter sw = new System.IO.StringWriter())
     {
        try
        {
           xs.Serialize(sw, output);
           objectAsXmlString = sw.ToString();
        }
        catch (Exception ex)
        {
           objectAsXmlString = ex.ToString();
        }
     }

     return objectAsXmlString;
  }

You'll see that the method might also return the exception rather than the serialized object, so you'll want to ensure that the objects you want to log are serializable.

Bernhard Hofmann
  • 10,321
  • 12
  • 59
  • 78
  • 2
    In the light of the features added to C# after you answered the question might be useful to point out that this implementation works nicely as an extension method. If applied to Object class and you reference the extension everywhere you need it, it could be a convenient way to call the function. – Nikita R. Nov 10 '13 at 09:14
  • I keep getting from this: ``Failed to access type 'System.__ComObject' failed``. Noob to c#, would appreciate help. – GuySoft Oct 18 '14 at 18:50
  • 1
    @GuySoft I suspect one of the properties on your object, or the object itself, is not serializable. – Bernhard Hofmann Oct 20 '14 at 09:15
  • You can't use this method on classes without a parameterless constructor, unfortunately. They are not serializable. – Jarekczek Sep 02 '16 at 13:14
22

ServiceStack.Text has a T.Dump() extension method that does exactly this, recursively dumps all properties of any type in a nice readable format.

Example usage:

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

and 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
  • 1
    It doesn't work for fields. The OP was explicitly asking about "entire objects". – Konrad Morawski Sep 20 '12 at 07:04
  • 5
    `He didn't say fields` - he said `entire objects`, which includes fields. He also mentioned Visual Studio's Immediate Window feature as an example what of he wanted to achieve (*"Just doing a simple `? objectname` will give me a nicely formatted 'dump' of the object"*). `? objectname` prints out all the fields as well. `This has been immensely helpful - one of my most used extension methods to date` - I'm not questioning that it's useful, only that it dumps entire objects. – Konrad Morawski Sep 20 '12 at 08:24
  • 3
    @KonradMorawski Wrong **entire objects** means a recursive dump of the object, NOT that it includes fields, which can easily lead to infinite recursive loop. You shouldn't assume what others are implying. My answer is both relevant and helpful, your down vote + comment is not. – mythz Sep 20 '12 at 09:08
  • 1
    @mythz yes of course you need to prevent a stack overflow (eg. every `Int32` field has a `MaxValue` field, which is an `Int32` itself...), that's a good point, but it doesn't change the fact that objects - and certainly entire ones - consist of fields, too, not just properties. What's more (you did not address that one), `? objectname` in the `Immediate Window` **does** display fields - without triggering an infinite loop. If that's about my downvote, I can withdraw it (if you let me by unlocking it, that is). I disagree in principle anyway. – Konrad Morawski Sep 20 '12 at 09:33
  • 1
    @mythz, by the way, there is a possibility of an infinite loop in case of properties as well, isn't it? If your class is `public class Nested { public Nested Child { get; set; } }`, you can do `var root = new Nested(); root.Child = root;` Unless I am misunderstanding you somehow on that one. – Konrad Morawski Sep 20 '12 at 09:36
  • 1
    @KonradMorawski You can prevent stackoverflows by maintaining state, which is something most and fast serializers don't do. You have a better chance of controlling the external shape of your type via public properties than is done with internal fields which are freely added without care. – mythz Sep 20 '12 at 09:43
  • 4
    -1 for essentially a link-only answer, though it looks great if I could use it! Perhaps I'm blind, but I can't find source via that link; the two upload folders are empty. Is the code too long to include in the answer? –  Jul 14 '14 at 19:26
  • Still relevant and helpful 11 years later. But apparently `ServiceStack.Text` was not free for some period. – James Hirschorn Aug 03 '21 at 19:26
20

Here is a stupidly simple way to write a flat object, nicely formatted:

using Newtonsoft.Json.Linq;

Debug.WriteLine("The object is " + JObject.FromObject(theObjectToDump).ToString());

What's going on is that the object is first converted to a JSON internal representation by JObject.FromObject, and then converted to JSON string by ToString. (And of course a JSON string is a very nice representation of a simple object, especially since ToString will include newlines and indents.) The "ToString" is of course extraneous (as it's implied by using + to concat a string and an object), but I kinda like to specify it here.

Hot Licks
  • 47,103
  • 17
  • 93
  • 151
  • 5
    JsonConvert.SerializeObject(apprec,Formatting.Indented) for comfortable reading in log – Tertium Aug 23 '16 at 23:04
  • 5
    HotLicks - I want to convey to you how important this contribution is to me right now. I have a requirement to provide an audit of what changed during an update and you've just taken my stress from 'panic' level back down to a manageable 'worry' level. Thank you sir, may you have a very long and blessed life – Iofacture May 16 '19 at 22:25
5

You could use reflection and loop through all the object properties, then get their values and save them to the log. The formatting is really trivial (you could use \t to indent an objects properties and its values):

MyObject
    Property1 = value
    Property2 = value2
    OtherObject
       OtherProperty = value ...
Ricardo Villamil
  • 5,031
  • 2
  • 30
  • 26
5

What I like doing is overriding ToString() so that I get more useful output beyond the type name. This is handy in the debugger, you can see the information you want about an object without needing to expand it.

Darryl Braaten
  • 5,229
  • 4
  • 36
  • 50
5

Following is another version that does the same thing (and handle nested properties), which I think is simpler (no dependencies on external libraries and can be modified easily to do things other than logging):

public class ObjectDumper
{
    public static string Dump(object obj)
    {
        return new ObjectDumper().DumpObject(obj);
    }

    StringBuilder _dumpBuilder = new StringBuilder();

    string DumpObject(object obj)
    {
        DumpObject(obj, 0);
        return _dumpBuilder.ToString();
    }

    void DumpObject(object obj, int nestingLevel = 0)
    {
        var nestingSpaces = "".PadLeft(nestingLevel * 4);

        if (obj == null)
        {
            _dumpBuilder.AppendFormat("{0}null\n", nestingSpaces);
        }
        else if (obj is string || obj.GetType().IsPrimitive)
        {
            _dumpBuilder.AppendFormat("{0}{1}\n", nestingSpaces, obj);
        }
        else if (ImplementsDictionary(obj.GetType()))
        {
            using (var e = ((dynamic)obj).GetEnumerator())
            {
                var enumerator = (IEnumerator)e;
                while (enumerator.MoveNext())
                {
                    dynamic p = enumerator.Current;

                    var key = p.Key;
                    var value = p.Value;
                    _dumpBuilder.AppendFormat("{0}{1} ({2})\n", nestingSpaces, key, value != null ? value.GetType().ToString() : "<null>");
                    DumpObject(value, nestingLevel + 1);
                }
            }
        }
        else if (obj is IEnumerable)
        {
            foreach (dynamic p in obj as IEnumerable)
            {
                DumpObject(p, nestingLevel);
            }
        }
        else
        {
            foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(obj))
            {
                string name = descriptor.Name;
                object value = descriptor.GetValue(obj);

                _dumpBuilder.AppendFormat("{0}{1} ({2})\n", nestingSpaces, name, value != null ? value.GetType().ToString() : "<null>");
                DumpObject(value, nestingLevel + 1);
            }
        }
    }

    bool ImplementsDictionary(Type t)
    {
        return t.GetInterfaces().Any(i => i.Name.Contains("IDictionary"));
    }
}
engineforce
  • 2,840
  • 1
  • 23
  • 17
  • 2
    this will die horribly if you have a `Date` property in your inner object ... just saying ... – Noctis Sep 07 '17 at 04:43
  • 1
    An unhandled exception of type 'System.StackOverflowException'. Using for a CharArea object – Pedro77 Mar 11 '22 at 19:22
5

Today you don't even need an external dependency. You can just use the built-in Microsoft Json Serializer.

using System;
using System.Text.Json;

namespace MyCompany.Core.Extensions
{
    public static class ObjectExtensions
    {
        public static string Dump(this object obj)
        {
            try
            {
                return JsonSerializer.Serialize(obj);
            }
            catch(Exception)
            {
                return string.Empty;
            }
        }
    }
}

Notice that you can pass a JsonSerializerOptions parameter to further customize the serialization to your liking:

enter image description here

Let's say you want to write the JSON indented for easy reading... we'd use:

   new JsonSerializerOptions { WriteIndented = true }

#######

Here's a good guide if you wish to migrate from NewtonSoft.Json to System.Text.Json:

Compare Newtonsoft.Json to System.Text.Json, and migrate to System.Text.Json

Leniel Maccaferri
  • 100,159
  • 46
  • 371
  • 480
4

I found a library called ObjectPrinter which allows to easily dump objects and collections to strings (and more). It does exactly what I needed.

Marek Dzikiewicz
  • 2,844
  • 1
  • 22
  • 24
3

You can write your own WriteLine method-

public static void WriteLine<T>(T obj)
    {
        var t = typeof(T);
        var props = t.GetProperties();
        StringBuilder sb = new StringBuilder();
        foreach (var item in props)
        {
            sb.Append($"{item.Name}:{item.GetValue(obj,null)}; ");
        }
        sb.AppendLine();
        Console.WriteLine(sb.ToString());
    }

Use it like-

WriteLine(myObject);

To write a collection we can use-

 var ifaces = t.GetInterfaces();
        if (ifaces.Any(o => o.Name.StartsWith("ICollection")))
        {

            dynamic lst = t.GetMethod("GetEnumerator").Invoke(obj, null);
            while (lst.MoveNext())
            {
                WriteLine(lst.Current);
            }
        }   

The method may look like-

 public static void WriteLine<T>(T obj)
    {
        var t = typeof(T);
        var ifaces = t.GetInterfaces();
        if (ifaces.Any(o => o.Name.StartsWith("ICollection")))
        {

            dynamic lst = t.GetMethod("GetEnumerator").Invoke(obj, null);
            while (lst.MoveNext())
            {
                WriteLine(lst.Current);
            }
        }            
        else if (t.GetProperties().Any())
        {
            var props = t.GetProperties();
            StringBuilder sb = new StringBuilder();
            foreach (var item in props)
            {
                sb.Append($"{item.Name}:{item.GetValue(obj, null)}; ");
            }
            sb.AppendLine();
            Console.WriteLine(sb.ToString());
        }
    }

Using if, else if and checking interfaces, attributes, base type, etc. and recursion (as this is a recursive method) in this way we may achieve an object dumper, but it is tedious for sure. Using the object dumper from Microsoft's LINQ Sample would save your time.

Ariful Islam
  • 664
  • 8
  • 12
3

All of the paths above assume that your objects are serializable to XML or JSON,
or you must implement your own solution.

But in the end you still get to the point where you have to solve problems like

  • recursion in objects
  • non-serializable objects
  • exceptions
  • ...

Plus log you want more information:

  • when the event happened
  • callstack
  • which threead
  • what was in the web session
  • which ip address
  • url
  • ...

There is the best solution that solves all of this and much more.
Use this Nuget package: Desharp.
For all types of applications - both web and desktop applications.
See it's Desharp Github documentation. It has many configuration options.

Just call anywhere:

Desharp.Debug.Log(anyException);
Desharp.Debug.Log(anyCustomValueObject);
Desharp.Debug.Log(anyNonserializableObject);
Desharp.Debug.Log(anyFunc);
Desharp.Debug.Log(anyFunc, Desharp.Level.EMERGENCY); // you can store into different files
  • it can save the log in nice HTML (or in TEXT format, configurable)
  • it's possible to write optionally in background thread (configurable)
  • it has options for max objects depth and max strings length (configurable)
  • it uses loops for iteratable objects and backward reflection for everything else,
    indeed for anything you can find in .NET environment.

I believe it will help.

Tom Flídr
  • 101
  • 5
3

So far a simplest and tidiest way for me is a serializer from YamlDotNet package.

using YamlDotNet.Serialization;

List<string> strings=new List<string>{"a","b","c"};
new Serializer().Serialize(strings)

will give you

- a
- b
- c

A more comprehensive example is here https://dotnetfiddle.net/KuV63n

montonero
  • 1,363
  • 10
  • 16
2

Based on @engineforce answer, I made this class that I'm using in a PCL project of a Xamarin Solution:

/// <summary>
/// Based on: https://stackoverflow.com/a/42264037/6155481
/// </summary>
public class ObjectDumper
{
    public static string Dump(object obj)
    {
        return new ObjectDumper().DumpObject(obj);
    }

    StringBuilder _dumpBuilder = new StringBuilder();

    string DumpObject(object obj)
    {
        DumpObject(obj, 0);
        return _dumpBuilder.ToString();
    }

    void DumpObject(object obj, int nestingLevel)
    {
        var nestingSpaces = "".PadLeft(nestingLevel * 4);

        if (obj == null)
        {
            _dumpBuilder.AppendFormat("{0}null\n", nestingSpaces);
        }
        else if (obj is string || obj.GetType().GetTypeInfo().IsPrimitive || obj.GetType().GetTypeInfo().IsEnum)
        {
            _dumpBuilder.AppendFormat("{0}{1}\n", nestingSpaces, obj);
        }
        else if (ImplementsDictionary(obj.GetType()))
        {
            using (var e = ((dynamic)obj).GetEnumerator())
            {
                var enumerator = (IEnumerator)e;
                while (enumerator.MoveNext())
                {
                    dynamic p = enumerator.Current;

                    var key = p.Key;
                    var value = p.Value;
                    _dumpBuilder.AppendFormat("{0}{1} ({2})\n", nestingSpaces, key, value != null ? value.GetType().ToString() : "<null>");
                    DumpObject(value, nestingLevel + 1);
                }
            }
        }
        else if (obj is IEnumerable)
        {
            foreach (dynamic p in obj as IEnumerable)
            {
                DumpObject(p, nestingLevel);
            }
        }
        else
        {
            foreach (PropertyInfo descriptor in obj.GetType().GetRuntimeProperties())
            {
                string name = descriptor.Name;
                object value = descriptor.GetValue(obj);

                _dumpBuilder.AppendFormat("{0}{1} ({2})\n", nestingSpaces, name, value != null ? value.GetType().ToString() : "<null>");

                // TODO: Prevent recursion due to circular reference
                if (name == "Self" && HasBaseType(obj.GetType(), "NSObject"))
                {
                    // In ObjC I need to break the recursion when I find the Self property
                    // otherwise it will be an infinite recursion
                    Console.WriteLine($"Found Self! {obj.GetType()}");
                }
                else
                {
                    DumpObject(value, nestingLevel + 1);
                }
            }
        }
    }

    bool HasBaseType(Type type, string baseTypeName)
    {
        if (type == null) return false;

        string typeName = type.Name;

        if (baseTypeName == typeName) return true;

        return HasBaseType(type.GetTypeInfo().BaseType, baseTypeName);
    }

    bool ImplementsDictionary(Type t)
    {
        return t is IDictionary;
    }
}
gianlucaparadise
  • 1,678
  • 20
  • 32
2

There is a new library, consumed via NuGet, called Dumpify.

It has Dump() extension methods to Dump objects into Console, Debug, Trace or any TextWriter you provide. It supports Properties, fields and private members and is highly configurable.

Examples:

var moaid = new Person { FirstName = "Moaid", LastName = "Hathot" };
var haneeni = new Person { FirstName = "Haneeni", LastName = "Shibli" };

moaid.Spouse = haneeni;
haneeni.Spouse = moaid;

moaid.Dump();

enter image description here

enter image description here

m1o2
  • 1,549
  • 2
  • 17
  • 27
1

Option 1: CSharpObjectFormatter from the Microsoft.CodeAnalysis.CSharp.Scripting.

C# REPL Command-Line Interface (CSI.EXE) and CSharpRepl use exactly this formatter.

using Microsoft.CodeAnalysis.CSharp.Scripting.Hosting;

var students = new[]
{
    new Student
    {
        Id = 1,
        Name = "Michael Hilus"
    },
    new Student
    {
        Id = 2,
        Name = "Alicia Keys"
    }
};

var formattedStudents = CSharpObjectFormatter.Instance.FormatObject(students);

Console.WriteLine(formattedStudents);

Output:

Student[2] { Student { Id=1, Name="Michael Hilus" }, Student { Id=2, Name="Alicia Keys" } }

But it's suitable for relatively simple objects, for example it can't dump the instance of System.Data.DataTable.

Option 2: vm.Aspects.Diagnostics.ObjectTextDumper - see documentation.

There is also very popular library: ObjectDumper.NET, it's an open source, but not free for commercial use.

Yevhen Cherkes
  • 626
  • 7
  • 10