From ac47631c859a98f6b3b0c5818a1a23da1ecfe13c Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 18 Jun 2026 17:24:44 +0000
Subject: [PATCH 1/5] Initial plan
From 037487c39bc462e4c9f55c2439fa3807ce73c085 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 18 Jun 2026 17:32:12 +0000
Subject: [PATCH 2/5] Handle JSON null token in collection readers
Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com>
---
...CollectionOfNullableStructsReaderWriter.cs | 5 ++
.../JsonCollectionOfReferencesReaderWriter.cs | 5 ++
.../JsonCollectionOfStructsReaderWriter.cs | 5 ++
.../Json/JsonCollectionReaderWriterTest.cs | 54 +++++++++++++++++++
4 files changed, 69 insertions(+)
create mode 100644 test/EFCore.Tests/Storage/Json/JsonCollectionReaderWriterTest.cs
diff --git a/src/EFCore/Storage/Json/JsonCollectionOfNullableStructsReaderWriter.cs b/src/EFCore/Storage/Json/JsonCollectionOfNullableStructsReaderWriter.cs
index 63f25b36e98..98a9bd2dc3d 100644
--- a/src/EFCore/Storage/Json/JsonCollectionOfNullableStructsReaderWriter.cs
+++ b/src/EFCore/Storage/Json/JsonCollectionOfNullableStructsReaderWriter.cs
@@ -55,6 +55,11 @@ public JsonCollectionOfNullableStructsReaderWriter(JsonValueReaderWriter
public override object FromJsonTyped(ref Utf8JsonReaderManager manager, object? existingObject = null)
{
+ if (manager.CurrentReader.TokenType == JsonTokenType.Null)
+ {
+ return null!;
+ }
+
IList collection;
if (IsReadOnly)
{
diff --git a/src/EFCore/Storage/Json/JsonCollectionOfStructsReaderWriter.cs b/src/EFCore/Storage/Json/JsonCollectionOfStructsReaderWriter.cs
index 323e296d005..9af50cf796d 100644
--- a/src/EFCore/Storage/Json/JsonCollectionOfStructsReaderWriter.cs
+++ b/src/EFCore/Storage/Json/JsonCollectionOfStructsReaderWriter.cs
@@ -36,6 +36,11 @@ public JsonCollectionOfStructsReaderWriter(JsonValueReaderWriter eleme
///
public override IEnumerable FromJsonTyped(ref Utf8JsonReaderManager manager, object? existingObject = null)
{
+ if (manager.CurrentReader.TokenType == JsonTokenType.Null)
+ {
+ return null!;
+ }
+
IList collection;
if (IsReadOnly)
{
diff --git a/test/EFCore.Tests/Storage/Json/JsonCollectionReaderWriterTest.cs b/test/EFCore.Tests/Storage/Json/JsonCollectionReaderWriterTest.cs
new file mode 100644
index 00000000000..19e6fab61d2
--- /dev/null
+++ b/test/EFCore.Tests/Storage/Json/JsonCollectionReaderWriterTest.cs
@@ -0,0 +1,54 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.ObjectModel;
+using Microsoft.EntityFrameworkCore.Storage.Json;
+
+namespace Microsoft.EntityFrameworkCore.Storage;
+
+public class JsonCollectionReaderWriterTest
+{
+ [ConditionalFact]
+ public void Reads_JSON_null_as_null_for_collection_of_references()
+ {
+ var readerWriter = new JsonCollectionOfReferencesReaderWriter, string>(JsonStringReaderWriter.Instance);
+
+ Assert.Null(readerWriter.FromJsonString("null"));
+ Assert.Equal(["a", "b"], (IEnumerable)readerWriter.FromJsonString("""["a","b"]"""));
+ }
+
+ [ConditionalFact]
+ public void Reads_JSON_null_as_null_for_read_only_collection_of_references()
+ {
+ var readerWriter = new JsonCollectionOfReferencesReaderWriter, string>(
+ JsonStringReaderWriter.Instance);
+
+ Assert.Null(readerWriter.FromJsonString("null"));
+ }
+
+ [ConditionalFact]
+ public void Reads_JSON_null_as_null_for_array_of_references()
+ {
+ var readerWriter = new JsonCollectionOfReferencesReaderWriter(JsonStringReaderWriter.Instance);
+
+ Assert.Null(readerWriter.FromJsonString("null"));
+ }
+
+ [ConditionalFact]
+ public void Reads_JSON_null_as_null_for_collection_of_structs()
+ {
+ var readerWriter = new JsonCollectionOfStructsReaderWriter, int>(JsonInt32ReaderWriter.Instance);
+
+ Assert.Null(readerWriter.FromJsonString("null"));
+ Assert.Equal([1, 2], (IEnumerable)readerWriter.FromJsonString("[1,2]"));
+ }
+
+ [ConditionalFact]
+ public void Reads_JSON_null_as_null_for_collection_of_nullable_structs()
+ {
+ var readerWriter = new JsonCollectionOfNullableStructsReaderWriter, int>(JsonInt32ReaderWriter.Instance);
+
+ Assert.Null(readerWriter.FromJsonString("null"));
+ Assert.Equal([1, null, 2], (IEnumerable)readerWriter.FromJsonString("[1,null,2]"));
+ }
+}
From 3a016548232387b854cefe63882e73faf0c89ef6 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 18 Jun 2026 17:35:10 +0000
Subject: [PATCH 3/5] Use Fact attribute for new tests
Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com>
---
.../Storage/Json/JsonCollectionReaderWriterTest.cs | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/test/EFCore.Tests/Storage/Json/JsonCollectionReaderWriterTest.cs b/test/EFCore.Tests/Storage/Json/JsonCollectionReaderWriterTest.cs
index 19e6fab61d2..7e042678d2e 100644
--- a/test/EFCore.Tests/Storage/Json/JsonCollectionReaderWriterTest.cs
+++ b/test/EFCore.Tests/Storage/Json/JsonCollectionReaderWriterTest.cs
@@ -8,7 +8,7 @@ namespace Microsoft.EntityFrameworkCore.Storage;
public class JsonCollectionReaderWriterTest
{
- [ConditionalFact]
+ [Fact]
public void Reads_JSON_null_as_null_for_collection_of_references()
{
var readerWriter = new JsonCollectionOfReferencesReaderWriter, string>(JsonStringReaderWriter.Instance);
@@ -17,7 +17,7 @@ public void Reads_JSON_null_as_null_for_collection_of_references()
Assert.Equal(["a", "b"], (IEnumerable)readerWriter.FromJsonString("""["a","b"]"""));
}
- [ConditionalFact]
+ [Fact]
public void Reads_JSON_null_as_null_for_read_only_collection_of_references()
{
var readerWriter = new JsonCollectionOfReferencesReaderWriter, string>(
@@ -26,7 +26,7 @@ public void Reads_JSON_null_as_null_for_read_only_collection_of_references()
Assert.Null(readerWriter.FromJsonString("null"));
}
- [ConditionalFact]
+ [Fact]
public void Reads_JSON_null_as_null_for_array_of_references()
{
var readerWriter = new JsonCollectionOfReferencesReaderWriter(JsonStringReaderWriter.Instance);
@@ -34,7 +34,7 @@ public void Reads_JSON_null_as_null_for_array_of_references()
Assert.Null(readerWriter.FromJsonString("null"));
}
- [ConditionalFact]
+ [Fact]
public void Reads_JSON_null_as_null_for_collection_of_structs()
{
var readerWriter = new JsonCollectionOfStructsReaderWriter, int>(JsonInt32ReaderWriter.Instance);
@@ -43,7 +43,7 @@ public void Reads_JSON_null_as_null_for_collection_of_structs()
Assert.Equal([1, 2], (IEnumerable)readerWriter.FromJsonString("[1,2]"));
}
- [ConditionalFact]
+ [Fact]
public void Reads_JSON_null_as_null_for_collection_of_nullable_structs()
{
var readerWriter = new JsonCollectionOfNullableStructsReaderWriter, int>(JsonInt32ReaderWriter.Instance);
From 6327d15b685644f05a2ee4b9fd81511852385533 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 19 Jun 2026 02:30:55 +0000
Subject: [PATCH 4/5] Fix regression: guard FromJsonString("null") call against
null jsonReaderWriter
Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com>
---
...CollectionOfNullableStructsReaderWriter.cs | 5 --
.../JsonCollectionOfReferencesReaderWriter.cs | 5 --
.../JsonCollectionOfStructsReaderWriter.cs | 5 --
.../Storage/Json/JsonValueReaderWriter.cs | 4 +-
.../JsonTypesTestBase.cs | 70 +++++++++++++++----
.../Json/JsonCollectionReaderWriterTest.cs | 54 --------------
6 files changed, 60 insertions(+), 83 deletions(-)
delete mode 100644 test/EFCore.Tests/Storage/Json/JsonCollectionReaderWriterTest.cs
diff --git a/src/EFCore/Storage/Json/JsonCollectionOfNullableStructsReaderWriter.cs b/src/EFCore/Storage/Json/JsonCollectionOfNullableStructsReaderWriter.cs
index 98a9bd2dc3d..63f25b36e98 100644
--- a/src/EFCore/Storage/Json/JsonCollectionOfNullableStructsReaderWriter.cs
+++ b/src/EFCore/Storage/Json/JsonCollectionOfNullableStructsReaderWriter.cs
@@ -55,11 +55,6 @@ public JsonCollectionOfNullableStructsReaderWriter(JsonValueReaderWriter
public override object FromJsonTyped(ref Utf8JsonReaderManager manager, object? existingObject = null)
{
- if (manager.CurrentReader.TokenType == JsonTokenType.Null)
- {
- return null!;
- }
-
IList collection;
if (IsReadOnly)
{
diff --git a/src/EFCore/Storage/Json/JsonCollectionOfStructsReaderWriter.cs b/src/EFCore/Storage/Json/JsonCollectionOfStructsReaderWriter.cs
index 9af50cf796d..323e296d005 100644
--- a/src/EFCore/Storage/Json/JsonCollectionOfStructsReaderWriter.cs
+++ b/src/EFCore/Storage/Json/JsonCollectionOfStructsReaderWriter.cs
@@ -36,11 +36,6 @@ public JsonCollectionOfStructsReaderWriter(JsonValueReaderWriter eleme
///
public override IEnumerable FromJsonTyped(ref Utf8JsonReaderManager manager, object? existingObject = null)
{
- if (manager.CurrentReader.TokenType == JsonTokenType.Null)
- {
- return null!;
- }
-
IList collection;
if (IsReadOnly)
{
diff --git a/src/EFCore/Storage/Json/JsonValueReaderWriter.cs b/src/EFCore/Storage/Json/JsonValueReaderWriter.cs
index 94daf881dcb..ba01afe7d51 100644
--- a/src/EFCore/Storage/Json/JsonValueReaderWriter.cs
+++ b/src/EFCore/Storage/Json/JsonValueReaderWriter.cs
@@ -79,7 +79,9 @@ public object FromJsonString(string json, object? existingObject = null)
var readerManager = new Utf8JsonReaderManager(new JsonReaderData(Encoding.UTF8.GetBytes(json)), null);
readerManager.MoveNext();
- return FromJson(ref readerManager, existingObject);
+ return readerManager.CurrentReader.TokenType == JsonTokenType.Null
+ ? null!
+ : FromJson(ref readerManager, existingObject);
}
///
diff --git a/test/EFCore.Specification.Tests/JsonTypesTestBase.cs b/test/EFCore.Specification.Tests/JsonTypesTestBase.cs
index c50aeef9b48..a77eb8527fb 100644
--- a/test/EFCore.Specification.Tests/JsonTypesTestBase.cs
+++ b/test/EFCore.Specification.Tests/JsonTypesTestBase.cs
@@ -2623,6 +2623,45 @@ protected class NullableEnumU64CollectionType
public IList EnumU64 { get; set; } = null!;
}
+ [Fact]
+ public virtual Task Can_read_write_null_value_of_collection_of_string_JSON_values()
+ => Can_read_and_write_JSON_value?>(
+ nameof(NullStringCollectionType.String),
+ null,
+ """{"Prop":null}""",
+ mappedCollection: true);
+
+ protected class NullStringCollectionType
+ {
+ public List? String { get; set; }
+ }
+
+ [Fact]
+ public virtual Task Can_read_write_null_value_of_collection_of_int_JSON_values()
+ => Can_read_and_write_JSON_value?>(
+ nameof(NullIntCollectionType.Int32),
+ null,
+ """{"Prop":null}""",
+ mappedCollection: true);
+
+ protected class NullIntCollectionType
+ {
+ public List? Int32 { get; set; }
+ }
+
+ [Fact]
+ public virtual Task Can_read_write_null_value_of_collection_of_nullable_int_JSON_values()
+ => Can_read_and_write_JSON_value?>(
+ nameof(NullNullableIntCollectionType.Int32),
+ null,
+ """{"Prop":null}""",
+ mappedCollection: true);
+
+ protected class NullNullableIntCollectionType
+ {
+ public List? Int32 { get; set; }
+ }
+
[Fact]
public virtual Task Can_read_write_collection_of_sbyte_values_with_converter_as_JSON_string()
=> Can_read_and_write_JSON_property_value(
@@ -3671,16 +3710,18 @@ protected virtual Task Can_read_and_write_JSON_value(
using var writer = new Utf8JsonWriter(stream);
var jsonReaderWriter = property.GetJsonValueReaderWriter()
- ?? property.GetTypeMapping().JsonValueReaderWriter!;
+ ?? property.GetTypeMapping().JsonValueReaderWriter;
- var toString = value == null ? null : jsonReaderWriter.ToJsonString(value);
- var fromString = toString == null ? null : jsonReaderWriter.FromJsonString(toString, existingObject);
+ var toString = value == null ? null : jsonReaderWriter!.ToJsonString(value);
+ var fromString = value == null
+ ? jsonReaderWriter?.FromJsonString("null")
+ : jsonReaderWriter!.FromJsonString(toString!, existingObject);
Assert.Equal(value, fromString);
- var actual = ToJsonPropertyString(jsonReaderWriter, value);
+ var actual = ToJsonPropertyString(jsonReaderWriter!, value);
Assert.Equal(json, actual);
- var fromJson = FromJsonPropertyString(jsonReaderWriter, actual, existingObject);
+ var fromJson = FromJsonPropertyString(jsonReaderWriter!, actual, existingObject);
if (existingObject != null)
{
Assert.Same(fromJson, existingObject);
@@ -3717,15 +3758,18 @@ protected virtual Task Can_read_and_write_JSON_value(
Assert.Equal(elementNullable, element.IsNullable);
- var comparer = element.GetValueComparer()!;
- var elementReaderWriter = element.GetJsonValueReaderWriter()!;
- foreach (var item in (IEnumerable)value!)
+ if (value != null)
{
- Assert.True(comparer.Equals(item, comparer.Snapshot(item)));
- Assert.True(
- comparer.Equals(
- item, FromJsonPropertyString(
- elementReaderWriter, ToJsonPropertyString(elementReaderWriter, item))));
+ var comparer = element.GetValueComparer()!;
+ var elementReaderWriter = element.GetJsonValueReaderWriter()!;
+ foreach (var item in (IEnumerable)value)
+ {
+ Assert.True(comparer.Equals(item, comparer.Snapshot(item)));
+ Assert.True(
+ comparer.Equals(
+ item, FromJsonPropertyString(
+ elementReaderWriter, ToJsonPropertyString(elementReaderWriter, item))));
+ }
}
AssertElementFacets(element, facets);
diff --git a/test/EFCore.Tests/Storage/Json/JsonCollectionReaderWriterTest.cs b/test/EFCore.Tests/Storage/Json/JsonCollectionReaderWriterTest.cs
deleted file mode 100644
index 7e042678d2e..00000000000
--- a/test/EFCore.Tests/Storage/Json/JsonCollectionReaderWriterTest.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Collections.ObjectModel;
-using Microsoft.EntityFrameworkCore.Storage.Json;
-
-namespace Microsoft.EntityFrameworkCore.Storage;
-
-public class JsonCollectionReaderWriterTest
-{
- [Fact]
- public void Reads_JSON_null_as_null_for_collection_of_references()
- {
- var readerWriter = new JsonCollectionOfReferencesReaderWriter, string>(JsonStringReaderWriter.Instance);
-
- Assert.Null(readerWriter.FromJsonString("null"));
- Assert.Equal(["a", "b"], (IEnumerable)readerWriter.FromJsonString("""["a","b"]"""));
- }
-
- [Fact]
- public void Reads_JSON_null_as_null_for_read_only_collection_of_references()
- {
- var readerWriter = new JsonCollectionOfReferencesReaderWriter, string>(
- JsonStringReaderWriter.Instance);
-
- Assert.Null(readerWriter.FromJsonString("null"));
- }
-
- [Fact]
- public void Reads_JSON_null_as_null_for_array_of_references()
- {
- var readerWriter = new JsonCollectionOfReferencesReaderWriter(JsonStringReaderWriter.Instance);
-
- Assert.Null(readerWriter.FromJsonString("null"));
- }
-
- [Fact]
- public void Reads_JSON_null_as_null_for_collection_of_structs()
- {
- var readerWriter = new JsonCollectionOfStructsReaderWriter, int>(JsonInt32ReaderWriter.Instance);
-
- Assert.Null(readerWriter.FromJsonString("null"));
- Assert.Equal([1, 2], (IEnumerable)readerWriter.FromJsonString("[1,2]"));
- }
-
- [Fact]
- public void Reads_JSON_null_as_null_for_collection_of_nullable_structs()
- {
- var readerWriter = new JsonCollectionOfNullableStructsReaderWriter, int>(JsonInt32ReaderWriter.Instance);
-
- Assert.Null(readerWriter.FromJsonString("null"));
- Assert.Equal([1, null, 2], (IEnumerable)readerWriter.FromJsonString("[1,null,2]"));
- }
-}
From 21d97f74f80f40817f4dfcff231ccb504db28222 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 19 Jun 2026 02:35:58 +0000
Subject: [PATCH 5/5] Fix return type of FromJsonString to object? and update
API baseline
Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com>
---
src/EFCore/EFCore.baseline.json | 2 +-
src/EFCore/Storage/Json/JsonValueReaderWriter.cs | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/EFCore/EFCore.baseline.json b/src/EFCore/EFCore.baseline.json
index 7c9812b71e2..7758e38cca6 100644
--- a/src/EFCore/EFCore.baseline.json
+++ b/src/EFCore/EFCore.baseline.json
@@ -17294,7 +17294,7 @@
"Member": "abstract object FromJson(ref Microsoft.EntityFrameworkCore.Storage.Json.Utf8JsonReaderManager manager, object? existingObject = null);"
},
{
- "Member": "object FromJsonString(string json, object? existingObject = null);"
+ "Member": "object? FromJsonString(string json, object? existingObject = null);"
},
{
"Member": "abstract void ToJson(System.Text.Json.Utf8JsonWriter writer, object? value);"
diff --git a/src/EFCore/Storage/Json/JsonValueReaderWriter.cs b/src/EFCore/Storage/Json/JsonValueReaderWriter.cs
index ba01afe7d51..c70814cb8cd 100644
--- a/src/EFCore/Storage/Json/JsonValueReaderWriter.cs
+++ b/src/EFCore/Storage/Json/JsonValueReaderWriter.cs
@@ -70,7 +70,7 @@ internal JsonValueReaderWriter()
/// The JSON to parse.
/// Can be used to update an existing object, rather than create a new one.
/// The read value.
- public object FromJsonString(string json, object? existingObject = null)
+ public object? FromJsonString(string json, object? existingObject = null)
{
if (string.IsNullOrWhiteSpace(json))
{
@@ -80,7 +80,7 @@ public object FromJsonString(string json, object? existingObject = null)
var readerManager = new Utf8JsonReaderManager(new JsonReaderData(Encoding.UTF8.GetBytes(json)), null);
readerManager.MoveNext();
return readerManager.CurrentReader.TokenType == JsonTokenType.Null
- ? null!
+ ? null
: FromJson(ref readerManager, existingObject);
}