runtime: JsonSerializer throwing "System.NotSupportedException : Collection was of a fixed size" for some array/collection parsing

Before adding SimpleTestClass MySampleTestClass { get; set; } property to the SimpleTestStruct struct and json test was running successfully, after adding object type JsonSerializer cannot parse it anymore Update: The original test failure is fixed with recent update, but the below case is still failing so updated the test case Exception: System.NotSupportedException : Collection was of a fixed size.

[Fact]
public void TestMethod1()
{
    var state = new AppState();
    string serialized = "{\"Values\":[1,2,3]}";
    object deserializedState = System.Text.Json.Serialization.JsonSerializer.Parse(serialized, typeof(AppState));
}

public class AppState
{
    public int[] Values { get; set; }
    public AppState()
    {
        Values = Array.Empty<int>();
    }
}

System.NotSupportedException : Collection was of a fixed size.
Stack Trace:
/_/src/System.Private.CoreLib/src/System/Array.CoreCLR.cs(482,0): at System.SZArrayHelper.Add[T](T value)
D:\dotnet\corefx\src\System.Text.Json\src\System\Text\Json\Serialization\JsonSerializer.Read.HandleArray.cs(264,0): 
at System.Text.Json.JsonSerializer.ApplyValueToEnumerable[TProperty](TProperty& value, ReadStack& state, Utf8JsonReader& reader)
D:\dotnet\corefx\src\System.Text.Json\src\System\Text\Json\Serialization\JsonPropertyInfoNotNullable.cs(67,0) : 
at System.Text.Json.JsonPropertyInfoNotNullable`3.ReadEnumerable(JsonTokenType tokenType, ReadStack& state, Utf8JsonReader& reader)
D:\dotnet\corefx\src\System.Text.Json\src\System\Text\Json\Serialization\JsonPropertyInfoNotNullable.cs(25,0)  : 
at System.Text.Json.JsonPropertyInfoNotNullable`3.Read(JsonTokenType tokenType, ReadStack& state, Utf8JsonReader& reader)
D:\dotnet\corefx\src\System.Text.Json\src\System\Text\Json\Serialization\JsonSerializer.Read.HandleValue.cs(28,0): 
at System.Text.Json.JsonSerializer.HandleValue(JsonTokenType tokenType, JsonSerializerOptions options, Utf8JsonReader& reader, ReadStack& state)
D:\dotnet\corefx\src\System.Text.Json\src\System\Text\Json\Serialization\JsonSerializer.Read.cs(48,0): 
at System.Text.Json.JsonSerializer.ReadCore(JsonSerializerOptions options, Utf8JsonReader& reader, ReadStack& readStack)
D:\dotnet\corefx\src\System.Text.Json\src\System\Text\Json\Serialization\JsonSerializer.Read.Helpers.cs(22,0) : 
at System.Text.Json.JsonSerializer.ReadCore(Type returnType, JsonSerializerOptions options, Utf8JsonReader& reader)
D:\dotnet\corefx\src\System.Text.Json\src\System\Text\Json\Serialization\JsonSerializer.Read.String.cs(74,0): 
at System.Text.Json.JsonSerializer.ParseCore(String json, Type returnType, JsonSerializerOptions options)
D:\dotnet\corefx\src\System.Text.Json\src\System\Text\Json\Serialization\JsonSerializer.Read.String.cs(31,0): 
at System.Text.Json.JsonSerializer.Parse[TValue](String json, JsonSerializerOptions options)
D:\dotnet\corefx\src\System.Text.Json\tests\Serialization\Object.ReadTests.cs(312,0): 
at System.Text.Json.Serialization.Tests.ObjectTests.ReadStructObjectValueTest()

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 1
  • Comments: 28 (25 by maintainers)

Most upvoted comments

public class ClassWithCollection
{
    public int[] Array { get; set; } = { 1, 2 };
    public ICollection<int> CollectionBackedByList { get; set; } = new List<int> { 1, 2 };
    public ICollection<int> CollectionBackedByArray { get; set; } = new int[] { 1, 2 };
}
const string json = @"{""Array"":[3,4,5,6],""CollectionBackedByList"":[3,4,5,6],""CollectionBackedByArray"":[3,4,5,6]}";

var resultJil = Jil.JSON.Deserialize<ClassWithCollection>(json);
var resultUtf8 = Utf8Json.JsonSerializer.Deserialize<ClassWithCollection>(json);
var resultNewton = Newtonsoft.Json.JsonConvert.DeserializeObject<ClassWithCollection>(json);

Tested using three popular serializers and they behave near the same. All of them replace a collection if it’s readonly (Array and CollectionBackedByArray cases), but if a collection is mutable (i.e. CollectionBackedByList) then only Newtonsoft appends new values. Utf8Json and Jil still replace the original object.

Probably we should align the behavior with Jil and Utf8Json because it’s common for all collections and more intuitive when there is a public setter (it says that you’re allowed to replace collection and should do it).

A more simple test-case. Note: The problem is resolved if you mark the property setter private.

[TestClass]
public class UnitTest1
{
	[TestMethod]
	public void TestMethod1()
	{
		var state = new AppState();
		string serialized = "{\"Values\":[1,2,3]}";
		object deserializedState = System.Text.Json.Serialization.JsonSerializer.Parse(serialized, typeof(AppState));
	}

	public class AppState
	{
		public int[] Values { get; set; }

		public AppState()
		{
			Values = Array.Empty<int>();
		}
	}
}

Sorry for not answering early, was very busy. I will take a look at it tomorrow and fix ASAP.

I ran into this issue even with public setter, and finally figured out the root cause: JsonSerializerOptions.IgnoreNullValues is true. Changing it to default false resolved my issue.

@ahsonkhan Because there are cases when you have your own collection which can’t be instantiated by a third party. Mostly it’s because a collection needs to know about it’s owner. So the best strategy here is:

  • replace a collections if there is a public setter;
  • add values to the collection if there is no public setter and the collection isn’t read-only;
  • ignore it otherwise.

only Newtonsoft appends new values. Utf8Json and Jil still replace the original object.

I agree that Newtonsoft is the odd one out. Replacing the original object makes more sense to do by default.

In the future if you want to extend behavior to support appending then you can add it as a setting.

@wcontayon i am not that familiar with the code base, so not sure why that llist has fixed size or if it is really need to be fixed size. But creating new list to add a new value is most likely not an option.

@ahsonkhan yes you are right, never mind

string json = @“{”“MyObject”“:{”“One”“}}”;

System.Text.Json.JsonException : ‘}’ is invalid after a property name. Expected a ‘:’. Path: $.MyObject | LineNumber: 0 | BytePositionInLine: 18.

That is expected. The json string you passed in is invalid JSON. A JSON object cannot contain just a string value. It must have property name: value pairs.