Skip to content

Json source generator should support JsonConverter on non-nullable and nullable types #66547

Closed
@meziantou

Description

@meziantou

Description

I have a custom JsonConverter like CustomConverter : JsonConverter<DateTime>. Without the source generator I can use the custom converter for DateTime and DateTime? properties. Using the source generator, the generated code doesn't compile for nullable properties.

I have to create 2 converters CustomConverter : JsonConverter<DateTime> and NullableCustomConverter : JsonConverter<DateTime?>.

Reproduction Steps

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>
</Project>
using System.Text.Json;
using System.Text.Json.Serialization;

Console.WriteLine(JsonSerializer.Serialize(new Sample()));

class Sample
{
    [JsonConverter(typeof(DateTimeOffsetToTimestampJsonConverter))]
    public DateTimeOffset Start { get; set; }

    [JsonConverter(typeof(DateTimeOffsetToTimestampJsonConverter))]
    public DateTimeOffset? End { get; set; } // Without this property, this is fine
}

class DateTimeOffsetToTimestampJsonConverter : JsonConverter<DateTimeOffset>
{
    internal const long TicksPerMicroseconds = 10;

    public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        var value = reader.GetInt64();
        return new DateTimeOffset(value * TicksPerMicroseconds, TimeSpan.Zero);
    }

    public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options)
    {
        writer.WriteNumberValue(value.Ticks / TicksPerMicroseconds);
    }
}

[JsonSourceGenerationOptions]
[JsonSerializable(typeof(Sample))]
internal sealed partial class SourceGenerationContext : JsonSerializerContext
{
}

Expected behavior

I can use the json source generator without compilation errors.

Actual behavior

error CS0029: Cannot implicitly convert type 'DateTimeOffsetToTimestampJsonConverter' to 'System.Text.Json.Serialization.JsonConverter<System.DateTimeOffset?>'

global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<global::System.Nullable<global::System.DateTimeOffset>> info1 = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<global::System.Nullable<global::System.DateTimeOffset>>()
{
    IsProperty = true,
    IsPublic = true,
    IsVirtual = false,
    DeclaringType = typeof(global::Sample),
    PropertyTypeInfo = jsonContext.NullableDateTimeOffset,
    Converter = new global::DateTimeOffsetToTimestampJsonConverter(), // 👈 Error on this line
    Getter = static (obj) => ((global::Sample)obj).End,
    Setter = static (obj, value) => ((global::Sample)obj).End = value!,
    IgnoreCondition = null,
    HasJsonInclude = false,
    IsExtensionData = false,
    NumberHandling = default,
    PropertyName = "End",
    JsonPropertyName = null
};

Regression?

No response

Known Workarounds

Use 2 different converters, one for non-nullable values and one for nullable values.

Configuration

.NET SDK (reflecting any global.json):
 Version:   7.0.100-preview.1.22110.4
 Commit:    129d2465c8

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.22000
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   C:\Program Files\dotnet\sdk\7.0.100-preview.1.22110.4\

Host (useful for support):
  Version: 7.0.0-preview.1.22076.8
  Commit:  405337939c

.NET SDKs installed:
  3.1.417 [C:\Program Files\dotnet\sdk]
  5.0.406 [C:\Program Files\dotnet\sdk]
  6.0.102 [C:\Program Files\dotnet\sdk]
  6.0.200 [C:\Program Files\dotnet\sdk]
  7.0.100-preview.1.22110.4 [C:\Program Files\dotnet\sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 3.1.22 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.1.23 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 5.0.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 5.0.14 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 5.0.15 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 6.0.2 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 7.0.0-preview.1.22109.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 3.1.22 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.1.23 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.14 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.15 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 6.0.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 6.0.2 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 7.0.0-preview.1.22076.8 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.WindowsDesktop.App 3.1.22 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 3.1.23 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 5.0.11 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 5.0.14 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 5.0.15 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 6.0.0 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 6.0.2 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 7.0.0-preview.1.22077.5 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

Other information

No response

Metadata

Metadata

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions