JSON.Net: Force Serialization of All Private Fields and All Fields in Sub-Classes

JSON.Net: Force serialization of all private fields and all fields in sub-classes

This should work:

var settings = new JsonSerializerSettings() { ContractResolver = new MyContractResolver() };
var json = JsonConvert.SerializeObject(obj, settings);

public class MyContractResolver : Newtonsoft.Json.Serialization.DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var props = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Select(p => base.CreateProperty(p, memberSerialization))
.Union(type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Select(f => base.CreateProperty(f, memberSerialization)))
.ToList();
props.ForEach(p => { p.Writable = true; p.Readable = true; });
return props;
}
}

Json.net serialize specific private field

There is no need to implement a custom DefaultContractResolver. The solution is to put [JsonProperty] on _hexes and [JsonIgnore] on all the other properties and fields.

Serilalize private field of base class without [DataContract]

As user2864740 mentioned, private properties of base types are not inherited (and they are not polymorphic), and thus currentType.GetProperties(..) doesn’t include them.

So, I iterated through .BaseType as there.

It fixed my trouble. Reproducible code example is below

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

namespace DerivedSerialization
{
internal class MyContractResolver : DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var properties = GetJsonProperties(type, memberSerialization);
foreach (var property in properties)
{
property.Ignored = false;
property.Readable = true;
property.Writable = true;
}
return properties;
}

private IList<JsonProperty> GetJsonProperties(Type type, MemberSerialization memberSerialization)
{
var properties = type
.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Where(p => p.CanRead && p.CanWrite)
.Select(p => base.CreateProperty(p, memberSerialization))
.Union(type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Select(f => base.CreateProperty(f, memberSerialization)));

if (type.BaseType == null)
return properties.Distinct(new JsonPropertyComparer()).ToList();

return properties.Union(GetJsonProperties(type.BaseType, memberSerialization))
.Distinct(new JsonPropertyComparer())
.ToList();
}
}

#region JsonPropertyComparer

internal class JsonPropertyComparer : IEqualityComparer<JsonProperty>
{
public bool Equals(JsonProperty left, JsonProperty right)
{
if (ReferenceEquals(left, right)) return true;

if (left == null || right == null) return false;

return left.PropertyName?.Equals(right.PropertyName) ?? right.PropertyName == null;
}

public int GetHashCode(JsonProperty obj)
{
return obj.PropertyName?.GetHashCode() ?? 0;
}
}

#endregion

internal class Program
{
class Base
{
public Base(int fieldValue)
{
_field = fieldValue;
}

private int _field;
}

class Derived : Base
{
public Derived(int fieldValue) : base(fieldValue)
{
}
}

public static void Main(string[] args)
{
var derived = new Derived(10);
var jsonSettings = new JsonSerializerSettings { ContractResolver = new MyContractResolver() };
var serialized = JsonConvert.SerializeObject(derived, jsonSettings);
Console.WriteLine(serialized);
}
}
}

Serializing a protected property in a generic class with Json.NET

By default, Json.NET serializes only properties with public getters (see docs). Your property Data is protected:

protected virtual T Data { get; set; }

In order for it to be serialized you should change it to public:

public virtual T Data { get; set; }

If changing access to public is not an option, there are ways to serialize non public items as well by implementing custom contract resolvers. Samples on how to use them can be found in answers to this question on SO - JSON.Net: Force serialization of all private fields and all fields in sub-classes

JSON Serializer object with internal properties

Mark the internal properties to be serialized with the [JsonProperty] attribute:

public class Foo
{
[JsonProperty]
internal int num1 { get; set; }
[JsonProperty]
internal double num2 { get; set; }

public string Description { get; set; }

public override string ToString()
{
if (!string.IsNullOrEmpty(Description))
return Description;

return base.ToString();
}
}

And then later, to test:

Foo f = new Foo();
f.Description = "Foo Example";
f.num1 = 101;
f.num2 = 202;
JsonSerializerSettings settings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All };

var jsonOutput = JsonConvert.SerializeObject(f, Formatting.Indented, settings);

Console.WriteLine(jsonOutput);

I get the following output:

{
"$type": "Tile.JsonInternalPropertySerialization.Foo, Tile",
"num1": 101,
"num2": 202.0,
"Description": "Foo Example"
}

(Where "Tile.JsonInternalPropertySerialization" and "Tile" are namespace and assembly names I am using).

As an aside, when using TypeNameHandling, do take note of this caution from the Newtonsoft docs:

TypeNameHandling should be used with caution when your application deserializes JSON from an external source. Incoming types should be validated with a custom SerializationBinder when deserializing with a value other than None.

For a discussion of why this may be necessary, see TypeNameHandling caution in Newtonsoft Json and and External json vulnerable because of Json.Net TypeNameHandling auto?.

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

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.



Related Topics



Leave a reply



Submit