Why are some members missing when trying to print an object by serializing to JSON?

How to print any arbitrary variable in C# so as to print all members?

I found three answers with the same technique:

  • https://stackoverflow.com/a/26181763/2125837 which suggests serializing with Json.NET among other answers, and
  • https://tech.io/playgrounds/2098/how-to-dump-objects-in-c/using-json-and-yaml-serializers,
  • https://www.codeproject.com/Articles/1194980/How-to-Dump-Object-for-Debugging-Purposes-in-Cshar

However, when I tried it out, with the following code,

using System;
using System.Collections.Generic;

using Newtonsoft.Json;

public static class Program
{
    public static void Main()
    {
        Test t1 = new Test(1, 2);
        {
            string json = JsonConvert.SerializeObject(t1, Formatting.Indented);
            Console.WriteLine(json);
        }
        Dump(t1);

        // Add 3 objects to a List.
        List<Test> list = new List<Test>();
        list.Add(new Test(1, 2));
        list.Add(new Test(3, 4));
        list.Add(new Test(5, 6));

        Console.WriteLine(list.ToString());
        {
            string json = JsonConvert.SerializeObject(list, Formatting.Indented);
            Console.WriteLine(json);
        }
    }

    public class Test
    {
        int A;
        int b;
        public Test(int _a, int _b)
        {
            A = _a;
            b = _b;
        }
    };

    public static void Dump<T>(this T x)
    {
        string json = JsonConvert.SerializeObject(x, Formatting.Indented);
        Console.WriteLine(json);
    }
}

All that I got are empty Test outputs:

{}
{}
System.Collections.Generic.List`1[Program+Test]
[
  {},
  {},
  {}
]

Why are all my class members missing when serializing to JSON with Json.NET?


By default Json.NET will only serialize public properties and fields. Your fields A and b are private. To cause nonpublic (private or internal) members to be serialized by Json.NET, you can:

  1. Make them public:

    public int A;
    public int b;
    

    However, stylistically, if you are going to make them be public, it's better to convert them to properties:

    public class Test
    {
        public int A { get; private set; }
        public int b { get; private set; }
        public Test(int a, int b)
        {
            this.A = a;
            this.b = b;
        }
    };
    

    Only the getters need be public for Json.NET to serialize them.

  2. Mark then with [JsonProperty]:

    [JsonProperty]
    int A;
    [JsonProperty]
    int b;
    

    This works for nonpublic properties as well.

  3. Mark your object with [DataContract] and your fields with [DataMember]:

    [DataContract]
    public class Test
    {
        [DataMember]
        int A;
        [DataMember]
        int b;
        public Test(int _a, int _b)
        {
            A = _a;
            b = _b;
        }
    };
    

    Note that data contract serialization is opt-in so you will need to mark every member to be serialized with [DataMember]. Kind of a nuisance but useful if you don't want your c# models to have a dependency on Json.NET.

    This also works for nonpublic properties.

  4. Mark your object with [JsonObject(MemberSerialization = MemberSerialization.Fields)]:

    [JsonObject(MemberSerialization = MemberSerialization.Fields)]
    public class Test
    {
        // Remainder as before...
    };
    

    As explained in the documentation for MemberSerialization, MemberSerialization.Fields ensures that

    All public and private fields are serialized. Members can be excluded using JsonIgnoreAttribute or NonSerializedAttribute. This member serialization mode can also be set by marking the class with SerializableAttribute and setting IgnoreSerializableAttribute on DefaultContractResolver to false.

    Of course this only causes nonpublic fields to be serialized, not nonpublic properties, but this may be what you want if your purpose is to print an arbitrary variable for debugging purposes.

  5. Use custom contract resolver that serializes all public and nonpublic fields.

    Several are shown at JSON.Net: Force serialization of all private fields and all fields in sub-classes which serialize both properties and fields that are public or private.

    Another, DeclaredFieldContractResolver from C# Serialize with JSON.NET inherited private fields, serializes only fields that are public or private by automatically assuming all objects are marked with MemberSerialization.Fields. You would use it like this:

    var settings = new JsonSerializerSettings
    {
        ContractResolver = DeclaredFieldContractResolver.Instance
    };
    var json = JsonConvert.SerializeObject(t1, Formatting.Indented, settings);
    
  6. Create a custom JsonConverter that serializes the necessary fields and properties. Since the fields are private it would need to be a nested type:

    public class Test
    {
        public class TestJsonConverter : JsonConverter
        {
            public override bool CanConvert(Type objectType)
            {
                return typeof(Test).IsAssignableFrom(objectType);
            }
    
            public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
            {
                var test = (Test)value;
    
                writer.WriteStartObject();
                writer.WritePropertyName(nameof(Test.A));
                serializer.Serialize(writer, test.A);
                writer.WritePropertyName(nameof(Test.b));
                serializer.Serialize(writer, test.b);
                writer.WriteEndObject();
            }
    
            public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
            {
                throw new NotImplementedException();
            }
        }
        // Remainder as before
    }
    

    And use it like:

    var json = JsonConvert.SerializeObject(t1, Formatting.Indented, new Test.TestJsonConverter());
    

    While this works, since the type is nested your model will still have a dependency on Json.NET, which makes option #2 the better choice.

If you are only dumping your object for debugging purposes, #5 might be the best option.