skip to Main Content

I have the following class:

public sealed class SomeClass
{
    [JsonConstructor()]
    public SomeClass(IEnumerable<string> myItems)
    {
        InternalMyItems = new Collection<string>(myItems.ToArray());
        MyItems = new ReadOnlyCollection<string>(InternalMyItems);
    }
    
    public IReadOnlyCollection<string> MyItems { get; }
    private Collection<string> InternalMyItems { get; }
}

Serialization seems to work fine:

{
  "MyItems": [
    "A",
    "B",
    "C"
  ]
}

Deserialization doesn’t seem to work. Ideally, I’d like to stick to using ReadOnlyCollection<T> and Collection<T> and not have to change to some other types. This sample code throws an exception when attempting to deserialize:

var options = new JsonSerializerOptions()
{
    WriteIndented = true
};

var items = new[] { "A", "B", "C" };
var instance = new SomeClass(items);

var json = JsonSerializer.Serialize(instance, options);

var copy = JsonSerializer.Deserialize<SomeClass>(json, options);

InvalidOperationException: Each parameter in the deserialization
constructor on type ‘UserQuery+SomeClass’ must bind to an object
property or field on deserialization. Each parameter name must match
with a property or field on the object. Fields are only considered
when ‘JsonSerializerOptions.IncludeFields’ is enabled. The match can
be case-insensitive.


Here is a .NET Fiddle example of the code running and giving an error: https://dotnetfiddle.net/vorOLX

2

Answers


  1. The type and name of constructor parameters must match the properties in the class. System.Text.Json also knows how to construct a IReadOnlyCollection<T> directly, so just use that type. For example, you could simply do this:

    public sealed class SomeClass
    {
        [JsonConstructor]
        public SomeClass(IReadOnlyCollection<string> myItems)
                       // ^^^^ matching type         
        {
            MyItems = myItems;
        }
    
        public IReadOnlyCollection<string> MyItems { get; }
    }
    
    Login or Signup to reply.
  2. From the docs:

    The parameter names of a parameterized constructor must match the property names and types.

    You can try using IReadOnlyCollection and match the types:

    public sealed class SomeClass
    {
        [JsonConstructor()]
        public SomeClass(IReadOnlyCollection<string> myItems)
        {
            InternalMyItems = new Collection<string>(myItems.ToArray());
            this.MyItems = new ReadOnlyCollection<string>(InternalMyItems);
        }
    
    
        public IReadOnlyCollection<string> MyItems { get; }
        private Collection<string> InternalMyItems { get; }
    }
    

    If needed you can keep the original ctor:

    public sealed class SomeClass
    {
        [System.Text.Json.Serialization.JsonConstructor()]
        public SomeClass(IReadOnlyCollection<string> myItems)
        {
            InternalMyItems = new Collection<string>(myItems.ToArray());
            this.MyItems = new ReadOnlyCollection<string>(InternalMyItems);
        }
    
        public SomeClass(IEnumerable<string> myItems)
        {
            InternalMyItems = new Collection<string>(myItems.ToArray());
            MyItems = new ReadOnlyCollection<string>(InternalMyItems);
        }
    
        public IReadOnlyCollection<string> MyItems { get; }
        private Collection<string> InternalMyItems { get; }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search