Newtonsoft.Json: Does not deserialize getter-only auto-properties

Getter-only properties in C# are readonly, but they should still be deserialized.

I.e. this does not work (but should) (Tested in v6.0.8):

public class MyClass
{
    public string MyProp { get; }
}

...

var r = JsonConvert.DeserializeObject<MyClass>("{\"MyProp\":\"awesome\"}");

Assert.AreEqual("awesome", r.MyProp);

However, changing the class to this makes it all work:

public class MyClass
{
    public string MyProp { get; private set; }
}

These two classes should work the same.

About this issue

  • Original URL
  • State: closed
  • Created 9 years ago
  • Reactions: 24
  • Comments: 43 (10 by maintainers)

Most upvoted comments

For the reason I said?

I disagree that this should be supported. If you have a getter-only auto property it should be assigned in the constructor or else add a private setter to the property. That way you’re not relying on C# auto property generation implementation details that may change from version to version.

@TylerBrinkley How do you propose we do the constructor route (in general) when just deserializing any object? For most users we run into, they expect a getter-only property to be populated when you serialize/deserialize it. That’s why we’ve added such functionality to Dapper and protobuf (@ekevoo’s question) .

Just adding a private setter isn’t so straightforward. That can easily be a performance impact since the JIT can no longer treat it as a readonly which enables a wide range of assumptions and optimizations.

I’m not arguing for this to be a default behavior, I’m simply saying it should be an easy-to-accomplish task. Yes, for the moment it is implementation dependent, so it’d be better if there was one copy of that implementation match in NuGet to update rather than every app doing the contracts themselves.

I agree this should be supported - we’ve added the same functionality to Protobuf and Dapper for all of the similar scenarios people run into.

@JamesNK: I use following code to write a value to a readonly property:

public class T
{
    public string Test { get; }
}

[Test]
public void TestSetReadonlyProperty()
{
    T testObject = new T();

    var property = testObject .GetType().GetProperty("Test");
    if (!property.CanWrite)
    {
        var field = testObject .GetType().GetField($"<{p.Name}>k__BackingField",
                                                   BindingFlags.NonPublic | BindingFlags.Instance);
        field?.SetValue(testObject , "ahah");
    }

    Assert.That(t.Test, Is.EqualTo("ahah"));
}

It should be supported as an optional (but default off) option, that way a user that knows what they are doing can use it if need be.

Newtonsoft seems to be incredibly clever about finding ways to populate read-only members. Consider the following scenario:

public class MyClass
{
    public MyClass(string prop1, string someName)
    {
        Prop1 = prop1;
        Prop2 = someName;
    }

    public string Prop1 { get; }
    public string Prop2 { get; }
}

When deserializing this class, Newtonsoft will be able to populate Prop1 because it sees a parameter named prop1 in the constructor:

Console.WriteLine(
    JsonConvert.SerializeObject(
        JsonConvert.DeserializeObject<MyClass>(
            "{\"Prop1\":\"foo\",\"Prop2\":\"bar\"}")));

// {"Prop1":"foo","Prop2":null}

Unfortunately this makes Newtonsoft easy to fool. You could do a little switcheroo in the constructor to have Newtonsoft deserialize the class incorrectly:

public MyClass(string prop1, string prop2)
{
    Prop2 = prop1;
    Prop1 = prop2;
}

It seems to me like it would be both easier and more accurate to have Newtonsoft just deserialize read-only members using reflection, but I understand that some developers would prefer to avoid reflection in their libraries to adhere to the principle of least surprise.

Why is this closed?

There is no way to know where the value for a setter only property comes from.

Fortunately, OP isn’t talking about setter-only properties…

This is about getter-only auto properties, which means there is a way to know where the value comes from: because it’s an auto property, it has a compiler-generated backing field ✨

The only difference between a getter-only auto property and one with a setter is that the backing field is readonly and thus can’t be assigned through the property outside the constructor.

The second class provides a way to set the the property that just happens to be private.

So does the first class. It just happens to be through a compiler-generated, private field 😉

Just because the property itself can’t be assigned, it doesn’t mean that Newtonsoft.Json couldn’t assign the backing field directly, like @xxMUROxx’s solution.

We could do something like this:

public class SampleClass
{
    [field: JsonProperty("Property")]
    [JsonIgnore]
    public string Property { get; } = "PropertyValue";
}

const string json = "{\"Property\":\"NEW-PropertyValue\"}";

var serializerSettings = new JsonSerializerSettings
{
    ContractResolver = new DefaultContractResolver
    {
	// this settings is needed to work with compiler-generated backing fields
        SerializeCompilerGeneratedMembers = true
    }
};

var deserializedSample = JsonConvert.DeserializeObject<SampleClass>(json, serializerSettings);

If you need to do this in older C# version or have no control over attributes, you could override CreateProperty() method of DefaultContractResolver with something like this:

protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
    var jsonProperty = base.CreateProperty(member, memberSerialization);

    if (jsonProperty.Writable == false)
    {
	// this uses compiler implementation details and may not work for all cases
	// better to use smarter approach like in BackingFieldResolver from Mono.Reflection library
        var fieldInfo = jsonProperty.DeclaringType.GetField($"<{jsonProperty.PropertyName}>k__BackingField",
            BindingFlags.NonPublic | BindingFlags.Instance);

        if (fieldInfo != null)
        {
            jsonProperty.ValueProvider = new ReflectionValueProvider(fieldInfo);
            jsonProperty.Writable = true;
        }
    }
        
    return jsonProperty;
}

First, I’ve been doing this for years now so if it changes you have to update but it works. Second, your first example is a readonly property and it’s not the same as your second example using expression bodied properties. public Count { get;} is not the same at all as public Count => count; for example and the same applies if you’re assigning a constant public Count => 5; for example. They work completely different. Finally, C# 9 is solving this with records anyway so all the naysays out there that don’t understand true immutability and the need for it won’t have to argue about it anymore. Making a class deserializable and not immutable is the same as making a string, which is immutable, a List of char just to deserializer it, as opposed to the immutable string. We should be allowed to deserialize true immutable types just like we do any other types and therefore not require the class to be designed to work differently just because we plan on using Newtonsoft to do the work. It’s ok though, Newton won’t do it then the community has and will continue to do it ourselves.

I agree with Salgat.

If I use a JsonSerializer to serialize an object, I expect using the same JsonSerializer to deserializer the resultant json to return the original object. (so long as the original object had no ignore properties)

@JamesNK I saw some workaround with using [JsonConstructor] https://www.michalkomorowski.com/2017/08/jsonnet-also-tricked-me.html

So… how exactly should this problem be approached? every other solution uses custom IContractResolver which doesn’t seem ideal to me.

Some interesting read https://www.productiverage.com/trying-to-set-a-readonly-autoproperty-value-externally-plus-a-little-benchmarkdotnet

I don’t think getter only property should be deserialized.

While one may be able to get away with in by using the backing field, it will soon break if the compiler change the name of the backing field in the future.

Also how could we expect to deserialize when there is no backing field as NewtonSoft will have no way of differentiating

public class Foo{
    public string Name{ get; }
}

from

public class Foo{
    public string Name=>"MyName";
}

Can this please be re-opened if it’s not already implemented. Having immutable DTO’s is more ideal than even private setters and this can be done via reflection.

@v-kydela Yes, your answer helped me fully explore the scope of the problem I had and the solutions available to me, however it was not a realistic solution for me. I felt I misused the get only auto property, and that requiring a constructor to be kept up to date was not ideal, for some of the very reasons you mentioned in your comment – it’s easy to fool Newtonsoft. Thank you though!

@BrianOstrander - Did you see my comment? Newtonsoft deserializes getter-only properties using constructor parameter names.

Is there a way to make this happen? I have a hard time believing that Newtonsoft can’t deserialize readonly properties. The cost on reflection should essentially be a one-time-per-app extremely minimal overhead:

using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;

namespace JsonTests {
    class Program {
        static void Main(string[] args) {

            var C = new Foo2();
            Setter<Foo2>.Set(C, "Name", "Hello");
            Setter<Foo2>.Set(C, "Age", 21);

            Console.WriteLine(C.Name);
            Console.WriteLine(C.Age);

        }

    }


    public static class Setter<T> {

        static Dictionary<string, Action<T, object>> Cache = new Dictionary<string, Action<T, object>>(StringComparer.InvariantCultureIgnoreCase);

        static Setter() {

            //var Props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetProperty | BindingFlags.SetProperty);
            var Props = typeof(T).GetProperties();

            foreach (var item in Props) {
                Cache[item.Name] = CreateSetter(item);
            }
        }

        public static void Set<V>(T Target, string PropName, V Value) {
            if (Cache.TryGetValue(PropName, out var Setter)) {
                Setter(Target, Value);
            }
        }

        public static Action<T, object> CreateSetter(PropertyInfo P) {
            var ret = default(Action<T, object>);

            ParameterExpression oParam = Expression.Parameter(typeof(T), "obj");
            ParameterExpression vParam = Expression.Parameter(typeof(object), "val");
            MethodCallExpression mce = Expression.Call(oParam, P.SetMethod, Expression.Convert(vParam, P.PropertyType));

            var action = Expression.Lambda<Action<T, object>>(mce, oParam, vParam);

            ret = action.Compile();

            return ret;
        }


        public static Action<T, TValue> CreateSetter<TValue>(Expression<Func<T, TValue>> memberLamda) {
            var ret = default(Action<T, TValue>);

            var memberSelectorExpression = memberLamda.Body as MemberExpression;
            if (memberSelectorExpression != null) {
                var property = memberSelectorExpression.Member as PropertyInfo;
                if (property != null) {

                    ParameterExpression oParam = Expression.Parameter(typeof(T), "obj");
                    ParameterExpression vParam = Expression.Parameter(typeof(TValue), "val");
                    MethodCallExpression mce = Expression.Call(oParam, property.SetMethod, vParam);
                    Expression<Action<T, TValue>> action = Expression.Lambda<Action<T, TValue>>(mce, oParam, vParam);

                    ret = action.Compile();


                }
            }

            return ret;
        }
    }


    public class Foo2 {
        public string Name { get; set; }
        public int Age { get; private set; }
    }

}

@ViceIce I thought that might work with adding a JsonIgnoreAttribute to the property but it still doesn’t because the backing field is readonly and it appears they aren’t supported either.

C# 7.3 has a new backing field attributes feature.

Shouldn’t it be possible to deserialize the auto-property with adding JsonPropertyAttribute to the backing field?

class Test {
  [field: JsonProperty("Num")]
  public int Num { get; }
}

With the non-default constructor. The idea is that once it’s constructed, you can serialize and preserve its state for retrieval elsewhere.