dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
14.91k stars 4.63k forks source link

System.Text.Json Source Generation fails to compile when explicit property names and ignored property names collide. #99988

Closed grbell-ms closed 1 month ago

grbell-ms commented 5 months ago

Description

Using source generation with a combination of case insensitive property names, ignored properties, and JsonPropertyNameAttribute with the same explicit name as an ignored property fails to compile.

Reproduction Steps

Try to compile this:

using System.Text.Json.Serialization;

namespace Foo
{
    [JsonSourceGenerationOptions(PropertyNameCaseInsensitive = true)]
    [JsonSerializable(typeof(Config2))]
    internal partial class SourceGenerationContext : JsonSerializerContext
    {
    }

    public sealed class Config2
    {
        [JsonIgnore]
        public List<string>? UserList { get; set; }

        [JsonPropertyName("userlist")]
        public List<string>? SystemTextJsonUserList { get; set; }

        [JsonIgnore]
        public List<string>? UserGroupsList { get; set; }

        [JsonPropertyName("usergroupslist")]
        public List<string>? SystemTextJsonUserGroupsList { get; set; }

        [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
        public List<string>? SystemTextJsonIPAddresses { get; set; }

        [JsonIgnore]
        public List<object>? QueryParams { get; set; }

        [JsonPropertyName("queryparams")]
        public List<object>? SystemTextJsonQueryParams { get; set; }
    }
}

Expected behavior

Compilation succeeds.

Actual behavior

Compilation fails with the error message: CS0128 A local variable or function named '__value_SystemTextJsonIPAddresses' is already defined in this scope ClassLibrary1 D:\ClassLibrary1\ClassLibrary1\System.Text.Json.SourceGeneration\System.Text.Json.SourceGeneration.JsonSourceGenerator\SourceGenerationContext.Config2.g.cs

The generated code looks like this:

private void Config2SerializeHandler(global::System.Text.Json.Utf8JsonWriter writer, global::Foo.Config2? value)
{
    if (value == null)
    {
        writer.WriteNullValue();
        return;
    }

    writer.WriteStartObject();

    writer.WritePropertyName(PropName_userlist);
    ListStringSerializeHandler(writer, ((global::Foo.Config2)value).SystemTextJsonUserList);
    global::System.Collections.Generic.List<string> __value_SystemTextJsonIPAddresses = ((global::Foo.Config2)value).SystemTextJsonIPAddresses;
    if (__value_SystemTextJsonIPAddresses != null)
    {
        writer.WritePropertyName(PropName_SystemTextJsonIPAddresses);
        ListStringSerializeHandler(writer, __value_SystemTextJsonIPAddresses);
    }
    global::System.Collections.Generic.List<string> __value_SystemTextJsonIPAddresses = ((global::Foo.Config2)value).SystemTextJsonIPAddresses;
    if (__value_SystemTextJsonIPAddresses != null)
    {
        writer.WritePropertyName(PropName_SystemTextJsonIPAddresses);
        ListStringSerializeHandler(writer, __value_SystemTextJsonIPAddresses);
    }

    writer.WriteEndObject();
}

Regression?

No.

Known Workarounds

Reorder the properties so that the ignored properties come after the corresponding explicitly named property.

public sealed class Config2
{
    [JsonPropertyName("userlist")]
    public List<string>? SystemTextJsonUserList { get; set; }

    [JsonIgnore]
    public List<string>? UserList { get; set; }

    [JsonIgnore]
    public List<string>? UserGroupsList { get; set; }

    [JsonPropertyName("usergroupslist")]
    public List<string>? SystemTextJsonUserGroupsList { get; set; }

    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
    public List<string>? SystemTextJsonIPAddresses { get; set; }

    [JsonIgnore]
    public List<object>? QueryParams { get; set; }

    [JsonPropertyName("queryparams")]
    public List<object>? SystemTextJsonQueryParams { get; set; }
}

Configuration

No response

Other information

No response

dotnet-policy-service[bot] commented 5 months ago

Tagging subscribers to this area: @dotnet/area-system-text-json, @gregsdennis See info in area-owners.md if you want to be subscribed.

eiriktsarpalis commented 5 months ago

I can reproduce. What's odd is that absolutely cannot reduce this to a smaller repro. Removing anything from your code makes the error just go away.