Runtime: System.Text.Json should support deserializing into mutable collections exposed via readonly members

Created on 2 Jul 2020  路  8Comments  路  Source: dotnet/runtime

Description

The pull request #34675 added support for private setters. And the default value for IgnoreReadOnlyProperties is false. So I would have assumed something like: [JsonInclude] public ObservableCollection<int> MyInt { get; } = new ObservableCollection<int>(); would work but it does not. It still requires the private set; to explicit written.

Configuration

  • .NET Core 5.0 (5.0.100-preview.5.20279.10)

Other information

    class Program
    {
        public static JsonSerializerOptions Options =>  new JsonSerializerOptions {IgnoreReadOnlyProperties = false};
        static void Main(string[] args)
        {
            var readonlySetter = new MyClass_Readonly();
            readonlySetter.MyInt.Add(1);
            var readonlyJson = JsonSerializer.Serialize(readonlySetter);
            Console.WriteLine("Readonly JSON: " + readonlyJson);
            var newReadonlySetter = JsonSerializer.Deserialize<MyClass_Readonly>(readonlyJson);
            Console.WriteLine("Readonly List Count: " + newReadonlySetter.MyInt.Count);

            var privateSetter = new MyClass_PrivateSetter();
            privateSetter.MyInt.Add(1);
            var privateJson = JsonSerializer.Serialize(privateSetter);
            Console.WriteLine("Private JSON: " + privateJson);
            var newPrivateSetter = JsonSerializer.Deserialize<MyClass_PrivateSetter>(privateJson);
            Console.WriteLine("Private List Count: " + newPrivateSetter.MyInt.Count);
        }
    }
    class MyClass_Readonly
    {
        [JsonInclude] public ObservableCollection<int> MyInt { get;  } = new ObservableCollection<int>();
    }
    class MyClass_PrivateSetter
    {
        [JsonInclude] public ObservableCollection<int> MyInt { get; private set; } = new ObservableCollection<int>();
    }

Output:

Readonly JSON: {"MyInt":[1]}
Readonly List Count: 0
Private JSON: {"MyInt":[1]}
Private List Count: 1
area-System.Text.Json

Most helpful comment

I see - you don't want to be able to actually set readonly properties, but to populate existing collections with deserialized data. (I assume Newtonsoft doesn't actually set the readonly property in your sample, but clears the collection and adds new items to it to sidestep problems discussed at length in the Newtonsoft issue.)

All 8 comments

I would think this behavior would be by design. A property defined as "{ get; }" doesn't have a private setter. It has literally _no setter at all_. There's nothing that can be reflection invoked.

Yes it would be a read-only property then. But guess what, IgnoreReadOnlyProperties is set to false. So I would think it would include read-only properties then, right? Might be a bug with private setter or with readonly properties. Both are now supported but in my example it just don't work.

_FYI: In Newtonsoft JSON it works out of the box. Just saying in case you would think it would be technically impossible, it is not_

FYI: In Newtonsoft JSON it works out of the box. Just saying in case you would think it would be technically impossible, it is not

It doesn't, according to this: https://github.com/JamesNK/Newtonsoft.Json/issues/703.

FYI: In Newtonsoft JSON it works out of the box. Just saying in case you would think it would be technically impossible, it is not

It doesn't, according to this: JamesNK/Newtonsoft.Json#703.

If you would have taken my example and ran it with Newtonsoft you would have noticed it does

    class Program
    {
        static void Main(string[] args)
        {
            var readonlySetter = new MyClass_Readonly();
            readonlySetter.MyInt.Add(1);
            var readonlyJson = JsonConvert.SerializeObject(readonlySetter);
            Console.WriteLine("Readonly JSON: " + readonlyJson);
            var newReadonlySetter = JsonConvert.DeserializeObject<MyClass_Readonly>(readonlyJson);
            Console.WriteLine("Readonly List Count: " + newReadonlySetter.MyInt.Count);

            var privateSetter = new MyClass_PrivateSetter();
            privateSetter.MyInt.Add(1);
            var privateJson = JsonConvert.SerializeObject(privateSetter);
            Console.WriteLine("Private JSON: " + privateJson);
            var newPrivateSetter = JsonConvert.DeserializeObject<MyClass_PrivateSetter>(privateJson);
            Console.WriteLine("Private List Count: " + newPrivateSetter.MyInt.Count);
        }
    }
    class MyClass_Readonly
    {
        [JsonProperty("a", DefaultValueHandling = DefaultValueHandling.Include)] public ObservableCollection<int> MyInt { get;  } = new ObservableCollection<int>();
    }
    class MyClass_PrivateSetter
    {
        [JsonProperty("b", DefaultValueHandling = DefaultValueHandling.Include)] public ObservableCollection<int> MyInt { get; private set; } = new ObservableCollection<int>();
    }

=== Output ===

Readonly JSON: {"a":[1]}
Readonly List Count: 1
Private JSON: {"b":[1]}
Private List Count: 1

Also this side note does not change my actual issue about both readonly and private setters are officially supported but practically it doesn't work. So let's keep focusing on System.Text.Json instead of Newtonsoft for now.

I see - you don't want to be able to actually set readonly properties, but to populate existing collections with deserialized data. (I assume Newtonsoft doesn't actually set the readonly property in your sample, but clears the collection and adds new items to it to sidestep problems discussed at length in the Newtonsoft issue.)

Yes exactly! 馃憤 I want the property to be a new collection with all the data from the deserialization (and if there is no data, it leaves the default behavior) I'm sorry, if I was misleading about this.

I've updated the issue title to more accurately reflect what's being requested.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

terrajobst picture terrajobst  路  158Comments

iSazonov picture iSazonov  路  139Comments

galvesribeiro picture galvesribeiro  路  185Comments

syeshchenko picture syeshchenko  路  199Comments

PureKrome picture PureKrome  路  157Comments