'Collection was of a fixed size.' exception: Is it an error or a bug in System.Text.Json.JsonSerializer?

Viewed 138

I have studied several SO answers citing this exception (like this one) without success. Also, my example to reproduce the behavior is the most minimal that I've seen. As a control for reproducing this issue, I have this constant string:

const string json =
@"{
    ""ArrayOfString"": [""Hello World""]
  }";

And ClassA:

class ClassA
{
    public string[] ArrayOfString { get; set; }
}

There are no issues deserializing ClassA whether I do it with Newtonsoft.Json or System.Text.Json.

ClassA classA;
classA = Newtonsoft.Json.JsonConvert.DeserializeObject<ClassA>(json);
classA = System.Text.Json.JsonSerializer.Deserialize<ClassA>(json);

Now consider ClassB where the same property has a backing store in BaseClass.

class ClassB : BaseClass
{
    public string[] ArrayOfString
    {
        get => new string[] { base.SingularString };
        set
        {
            if((value != null) && (value.Length == 1))
            {
                base.SingularString = value[0];
            }
        }
    }
}
class BaseClass
{
    public string SingularString { get; set; } = "Hello World";
}

It doesn't seem like a big ask to have ClassB deserialize from the same json source.

ClassB classB;
classB = Newtonsoft.Json.JsonConvert.DeserializeObject<ClassB>(json);
classB = System.Text.Json.JsonSerializer.Deserialize<ClassB>(json);

Newtonsoft doesn't have any beef doing this:

newtonsoft watch

However, System.Text.Json throws when asked to do the same thing:

system.text.json

Does anyone have first-hand knowledge of this bug which was closed more than 2 years ago? Is this a reemergence of that? Or am simply making an error of some kind? And yeah, I get it. The collection is a fixed size but why would that matter?

1 Answers

This is a bug that was fixed in .NET 5. See pull Replace collection instances on deserialize #39442. A working .NET 6 fiddle can be seen here: https://dotnetfiddle.net/UhRYJP.

If you must develop against .NET Core 3.1, your options would appear to include:

  1. Introduce a custom converter for ClassB.

  2. Use a List<string> instead of a string [] for ArrayOfString:

    class ClassB : BaseClass
    {
        public List<string> ArrayOfString
        {
            get => new List<string>() { base.SingularString };
            set
            {
                if((value != null) && (value.Count == 1))
                {
                    base.SingularString = value[0];
                }
            }
        }
    }
    

    (Note that if you are still targeting both System.Text.Json and Newtonsoft, List<string> will not work with the latter. See e.g. Why are all the collections in my POCO are null when deserializing some valid json with the .NET Newtonsoft.Json component for why.)

Related