Skip to content

Using JSON Source Gen still roots all JsonConverters #52662

Description

@eerhardt

Using System.Text.Json source generation in my application isn't providing for size reductions in the app because all the JsonConverters are still being rooted in the app.

Repro

using System;
using System.Text.Json;
using System.Text.Json.Serialization;
using ConsoleApp13;
using ConsoleApp13.JsonSourceGeneration;

[assembly: JsonSerializable(typeof(WeatherForecast))]

namespace ConsoleApp13
{
    class Program
    {
        static void Main(string[] args)
        {
            string f = @"{""TemperatureC"":0,""Summary"":""hi""}";

            var forecast = JsonSerializer.Deserialize(f, new JsonContext().WeatherForecast);
            Console.WriteLine(forecast);
        }
    }

    public record WeatherForecast
    {
        public DateTime Date { get; set; }

        public int TemperatureC { get; set; }

        public string Summary { get; set; }

        public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
    }
}

dotnet publish -r win-x64 -c Release -p:PublishTrimmed=true -p:DebuggerSupport=false

View the outputted assemblies, and notice that System.Text.Json.dll is ~289KB, it still has all its JsonConverters in it, and System.Collections.Immutable.dll is in my app, even though I don't use it.

Analysis

From looking at the results, the reason everything is still rooted is because of the following call graph:

image

This is what is rooting ObjectConverter. Then inside ObjectConverter.Read, it uses JsonNodeConverter.Instance:

public override object? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (options.UnknownTypeHandling == JsonUnknownTypeHandling.JsonElement)
{
return JsonElement.ParseValue(ref reader);
}
return JsonNodeConverter.Instance.Read(ref reader, typeToConvert, options);

This brings in all of the JsonNode hierarchy, including JsonValue<T>, which overrides ToString() to call JsonSerializer.Serialize without source gen information:

JsonSerializer.Serialize(writer, _value, _value!.GetType(), options);

That this point, since JsonSerializer.Serialize is called without source gen information, it now roots all JsonConverters, and the size of the app grows by a lot.

cc @layomia @steveharter @marek-safar @vitek-karas @CoffeeFlux

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions