Skip to content

Serialization of ReadOnlyMemory<byte> with [JsonIgnore] attribute in .NET 6 and .NET 7 RC1 #76807

@MartyIX

Description

@MartyIX

Description

JsonSerializer.Serialize(..) method works differently in .NET 6 and in .NET 7 for ReadOnlyMemory<byte> property with [JsonIgnore] attribute.

Reproduction Steps

The issue can be reproduced using the following two files

JsonSerializerRepro.csproj:

<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <OutputType>Exe</OutputType>
		
	<!-- Works with .NET 6 -->
        <TargetFramework>net6.0</TargetFramework>
		
	<!-- Fails with .NET 7 -->
        <!--<TargetFramework>net7.0</TargetFramework>-->
        <ImplicitUsings>disable</ImplicitUsings>
        <Nullable>enable</Nullable>
    </PropertyGroup>
</Project>

Program.cs:

using System;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace JsonSerializeRepro;

public static class Program
{
    public static void Main(string[] args)
    {
        ReadOnlyMemory<byte> binaryData = Array.Empty<byte>();
        InstallScriptRequest installScriptRequest = new(binaryData, upgrade: true);

        string json = JsonSerializer.Serialize(installScriptRequest);
        Console.WriteLine(json);
    }
}

public class InstallScriptRequest
{
    [JsonIgnore]
    public int BinaryDataMaxSize => 1024 * 1024;

    [JsonIgnore]
    public ReadOnlyMemory<byte> BinaryData { get; set; }

    public bool Upgrade { get; }

    public InstallScriptRequest(ReadOnlyMemory<byte> binaryData, bool upgrade)
    {
        this.BinaryData = binaryData;
        this.Upgrade = upgrade;
    }
}

Expected behavior

When run as .NET 6 project, I get: {"Upgrade":true}

Actual behavior

When run as .NET 7 project (RC1), I get:

Unhandled exception. System.InvalidOperationException: The type 'System.ReadOnlySpan`1[System.Byte]' of property 'Span' on type 'System.ReadOnlyMemory`1[System.Byte]' is invalid for serialization or deserialization because it is a pointer type, is a ref struct, or contains generic parameters that have not been replaced by specific types.
   at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_CannotSerializeInvalidType(Type typeToConvert, Type declaringType, MemberInfo memberInfo)
   at System.Text.Json.Serialization.Metadata.ReflectionJsonTypeInfo`1.CreateProperty(Type typeToConvert, MemberInfo memberInfo, JsonSerializerOptions options, Boolean shouldCheckForRequiredKeyword)
   at System.Text.Json.Serialization.Metadata.ReflectionJsonTypeInfo`1.CacheMember(Type typeToConvert, MemberInfo memberInfo, Boolean& propertyOrderSpecified, Dictionary`2& ignoredMembers, Boolean shouldCheckForRequiredKeyword)
   at System.Text.Json.Serialization.Metadata.ReflectionJsonTypeInfo`1.AddPropertiesAndParametersUsingReflection()
   at System.Text.Json.Serialization.Metadata.ReflectionJsonTypeInfo`1..ctor(JsonConverter converter, JsonSerializerOptions options)
   at System.Text.Json.Serialization.JsonConverter`1.CreateReflectionJsonTypeInfo(JsonSerializerOptions options)
   at System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver.CreateJsonTypeInfo(Type type, JsonSerializerOptions options)
   at System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver.GetTypeInfo(Type type, JsonSerializerOptions options)
   at System.Text.Json.JsonSerializerOptions.GetTypeInfoNoCaching(Type type)
   at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at System.Text.Json.JsonSerializerOptions.GetTypeInfoInternal(Type type, Boolean ensureConfigured, Boolean resolveIfMutable)
   at System.Text.Json.Serialization.Metadata.JsonPropertyInfo.Configure()
   at System.Text.Json.Serialization.Metadata.JsonTypeInfo.InitializePropertyCache()
   at System.Text.Json.Serialization.Metadata.JsonTypeInfo.Configure()
   at System.Text.Json.Serialization.Metadata.JsonTypeInfo.<EnsureConfigured>g__ConfigureLocked|138_0()
   at System.Text.Json.JsonSerializerOptions.GetTypeInfoInternal(Type type, Boolean ensureConfigured, Boolean resolveIfMutable)
   at System.Text.Json.JsonSerializer.GetTypeInfo(JsonSerializerOptions options, Type inputType)
   at System.Text.Json.JsonSerializer.GetTypeInfo[T](JsonSerializerOptions options)
   at System.Text.Json.JsonSerializer.Serialize[TValue](TValue value, JsonSerializerOptions options)
   at JsonSerializeRepro.Program.Main(String[] args) in C:\Users\USER\JsonSerializeRepro\JsonSerializeRepro\Program.cs:line 14

Regression?

Maybe it's a regression. I'm not sure.

Known Workarounds

Implement custom JSON converter

using System;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace JsonSerializeRepro;

public static class Program
{
    public static void Main(string[] args)
    {
        ReadOnlyMemory<byte> binaryData = Array.Empty<byte>();
        InstallScriptRequest installScriptRequest = new(binaryData, upgrade: true);

        JsonSerializerOptions serializationOptions = new();
        serializationOptions.Converters.Add(new InstallScriptRequestConverter());

        string json = JsonSerializer.Serialize(installScriptRequest, serializationOptions);
        Console.WriteLine(json);
    }
}

public class InstallScriptRequestConverter : JsonConverter<InstallScriptRequest>
{
    /// <inheritdoc/>
    public override InstallScriptRequest Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        throw new NotImplementedException();
    }

    /// <inheritdoc/>
    public override void Write(Utf8JsonWriter writer, InstallScriptRequest value, JsonSerializerOptions options)
    {
        writer.WriteStartObject();
        writer.WritePropertyName("upgrade");
        writer.WriteBooleanValue(value.Upgrade);
        writer.WriteEndObject();
    }
}

public class InstallScriptRequest
{
    /// <inheritdoc/>
    [JsonIgnore]
    public int BinaryDataMaxSize => 1024 * 1024;

    /// <summary>ZIP archive that contains the script package to install.</summary>
    [JsonIgnore]
    public ReadOnlyMemory<byte> BinaryData { get; set; }

    public bool Upgrade { get; }

    public InstallScriptRequest(ReadOnlyMemory<byte> binaryData, bool upgrade)
    {
        this.BinaryData = binaryData;
        this.Upgrade = upgrade;
    }
}

Configuration

Tested on Windows 11. However, I don't believe it's a platform-specific issue.

Other information

No response

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions