diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/MemoryConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/MemoryConverter.cs index 55f145e535479..670ab037367a8 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/MemoryConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/MemoryConverter.cs @@ -2,12 +2,30 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace System.Text.Json.Serialization.Converters { internal sealed class MemoryConverter : JsonCollectionConverter, T> { internal override bool CanHaveMetadata => false; + public override bool HandleNull => true; + + internal override bool OnTryRead( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options, + scoped ref ReadStack state, + out Memory value) + { + if (reader.TokenType is JsonTokenType.Null) + { + value = default; + return true; + } + + return base.OnTryRead(ref reader, typeToConvert, options, ref state, out value); + } protected override void Add(in T value, ref ReadStack state) { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ReadOnlyMemoryConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ReadOnlyMemoryConverter.cs index b8778c7736baa..8ca1ef71db3fb 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ReadOnlyMemoryConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ReadOnlyMemoryConverter.cs @@ -2,12 +2,30 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace System.Text.Json.Serialization.Converters { internal sealed class ReadOnlyMemoryConverter : JsonCollectionConverter, T> { internal override bool CanHaveMetadata => false; + public override bool HandleNull => true; + + internal override bool OnTryRead( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options, + scoped ref ReadStack state, + out ReadOnlyMemory value) + { + if (reader.TokenType is JsonTokenType.Null) + { + value = default; + return true; + } + + return base.OnTryRead(ref reader, typeToConvert, options, ref state, out value); + } protected override void Add(in T value, ref ReadStack state) { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/JsonMetadataServicesConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/JsonMetadataServicesConverter.cs index debb3f0d77c09..88303c6284d1f 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/JsonMetadataServicesConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/JsonMetadataServicesConverter.cs @@ -20,6 +20,7 @@ internal sealed class JsonMetadataServicesConverter : JsonResumableConverter< internal override Type? KeyType => Converter.KeyType; internal override Type? ElementType => Converter.ElementType; + public override bool HandleNull { get; } internal override bool ConstructorIsParameterized => Converter.ConstructorIsParameterized; internal override bool SupportsCreateObjectDelegate => Converter.SupportsCreateObjectDelegate; @@ -29,8 +30,16 @@ internal sealed class JsonMetadataServicesConverter : JsonResumableConverter< public JsonMetadataServicesConverter(JsonConverter converter) { - ConverterStrategy = converter.ConverterStrategy; Converter = converter; + ConverterStrategy = converter.ConverterStrategy; + IsInternalConverter = converter.IsInternalConverter; + IsInternalConverterForNumberType = converter.IsInternalConverterForNumberType; + CanBePolymorphic = converter.CanBePolymorphic; + + // Ensure HandleNull values reflect the exact configuration of the source converter + HandleNullOnRead = converter.HandleNullOnRead; + HandleNullOnWrite = converter.HandleNullOnWrite; + HandleNull = converter.HandleNullOnWrite; } internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, scoped ref ReadStack state, out T? value) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/MemoryByteConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/MemoryByteConverter.cs index 20536c81b3627..0efa7e71a13ac 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/MemoryByteConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/MemoryByteConverter.cs @@ -5,9 +5,11 @@ namespace System.Text.Json.Serialization.Converters { internal sealed class MemoryByteConverter : JsonConverter> { + public override bool HandleNull => true; + public override Memory Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - return reader.GetBytesFromBase64(); + return reader.TokenType is JsonTokenType.Null ? default : reader.GetBytesFromBase64(); } public override void Write(Utf8JsonWriter writer, Memory value, JsonSerializerOptions options) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/ReadOnlyMemoryByteConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/ReadOnlyMemoryByteConverter.cs index 68b7c1f033354..48eddafefba20 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/ReadOnlyMemoryByteConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/ReadOnlyMemoryByteConverter.cs @@ -5,9 +5,11 @@ namespace System.Text.Json.Serialization.Converters { internal sealed class ReadOnlyMemoryByteConverter : JsonConverter> { + public override bool HandleNull => true; + public override ReadOnlyMemory Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - return reader.GetBytesFromBase64(); + return reader.TokenType is JsonTokenType.Null ? default : reader.GetBytesFromBase64(); } public override void Write(Utf8JsonWriter writer, ReadOnlyMemory value, JsonSerializerOptions options) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonResumableConverterOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonResumableConverterOfT.cs index fcf782a2e4fe7..81bceda8d8404 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonResumableConverterOfT.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonResumableConverterOfT.cs @@ -12,6 +12,8 @@ namespace System.Text.Json.Serialization /// internal abstract class JsonResumableConverter : JsonConverter { + public override bool HandleNull => false; + public sealed override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (options is null) @@ -51,7 +53,5 @@ public sealed override void Write(Utf8JsonWriter writer, T value, JsonSerializer throw; } } - - public sealed override bool HandleNull => false; } } diff --git a/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Memory.cs b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Memory.cs index bf6e148cde18c..58525300624be 100644 --- a/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Memory.cs +++ b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Memory.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Text.Json.Tests; using System.Threading.Tasks; using Xunit; @@ -104,6 +103,22 @@ public async Task DeserializeMemoryByteAsync() AssertExtensions.SequenceEqual(s_testData, readOnlyMemory.Span); } + [Fact] + public async Task DeserializeNullAsMemory() + { + ReadOnlyMemory readOnlyMemOfInt = await Serializer.DeserializeWrapper>("null"); + Assert.True(readOnlyMemOfInt.IsEmpty); + + Memory memOfInt = await Serializer.DeserializeWrapper>("null"); + Assert.True(memOfInt.IsEmpty); + + ReadOnlyMemory readOnlyMemOfByte = await Serializer.DeserializeWrapper>("null"); + Assert.True(readOnlyMemOfByte.IsEmpty); + + Memory memOfByte = await Serializer.DeserializeWrapper>("null"); + Assert.True(memOfByte.IsEmpty); + } + [Fact] public async Task SerializeMemoryByteClassAsync() {