Ricardo Peres
Ricardo Peres

Reputation: 14555

Exception about missing TypeInfoResolver while using BenchmarkDotNet to perform a JSON source generator serialization

I am getting an exception:

> System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
 ---> System.InvalidOperationException: TypeInfoResolver 'JsonBenchmark.DataSourceGenerationContext' did not provide property metadata for type 'JsonBenchmark.Data'.
   at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_NoMetadataForTypeProperties(IJsonTypeInfoResolver resolver, Type type)
   at System.Text.Json.Serialization.Converters.ObjectWithParameterizedConstructorConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
   at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
   at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, T& value, JsonSerializerOptions options, ReadStack& state)
   at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.Deserialize(Utf8JsonReader& reader, ReadStack& state)
   at System.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan`1 utf8Json, JsonTypeInfo`1 jsonTypeInfo, Nullable`1 actualByteCount)
   at System.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan`1 json, JsonTypeInfo`1 jsonTypeInfo)
   at System.Text.Json.JsonSerializer.Deserialize[TValue](String json, JsonTypeInfo`1 jsonTypeInfo)
   at JsonBenchmark.Benchmark.SystemTextJsonDeserializeSourceGenerator() in C:\Users\Ricardo.Peres\source\repos\JsonBenchmark\JsonBenchmark\Benchmark.cs:line 66
   at BenchmarkDotNet.Toolchains.InProcess.NoEmit.BenchmarkActionFactory.BenchmarkActionVoid.WorkloadActionUnroll(Int64 repeatCount)
   at BenchmarkDotNet.Engines.Engine.Measure(Action`1 action, Int64 invokeCount)
   at BenchmarkDotNet.Engines.Engine.RunIteration(IterationData data)
   at BenchmarkDotNet.Engines.EngineFactory.Jit(Engine engine, Int32 jitIndex, Int32 invokeCount, Int32 unrollFactor)
   at BenchmarkDotNet.Engines.EngineFactory.CreateReadyToRun(EngineParameters engineParameters)
   at BenchmarkDotNet.Toolchains.InProcess.NoEmit.InProcessNoEmitRunner.Runnable.RunCore(IHost host, BenchmarkCase benchmarkCase)
   at InvokeStub_Runnable.RunCore(Object, Span`1)
   at System.Reflection.MethodBaseInvoker.InvokeWithFewArgs(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   --- End of inner exception stack trace ---
   at System.Reflection.MethodBaseInvoker.InvokeWithFewArgs(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
   at BenchmarkDotNet.Toolchains.InProcess.NoEmit.InProcessNoEmitRunner.Run(IHost host, BenchmarkCase benchmarkCase)
Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.
   at BenchmarkDotNet.Toolchains.Results.ExecuteResult.LogIssues(ILogger logger, BuildResult buildResult)
   at BenchmarkDotNet.Running.BenchmarkRunnerClean.RunExecute(ILogger logger, BenchmarkCase benchmarkCase, BenchmarkId benchmarkId, IToolchain toolchain, BuildResult buildResult, IResolver resolver, IDiagnoser diagnoser, Int32 launchIndex)
   at BenchmarkDotNet.Running.BenchmarkRunnerClean.Execute(ILogger logger, BenchmarkCase benchmarkCase, BenchmarkId benchmarkId, IToolchain toolchain, BuildResult buildResult, IResolver resolver)
   at BenchmarkDotNet.Running.BenchmarkRunnerClean.RunCore(BenchmarkCase benchmarkCase, BenchmarkId benchmarkId, ILogger logger, IResolver resolver, BuildResult buildResult)
   at BenchmarkDotNet.Running.BenchmarkRunnerClean.Run(BenchmarkRunInfo benchmarkRunInfo, Dictionary`2 buildResults, IResolver resolver, ILogger logger, EventProcessor eventProcessor, List`1 artifactsToCleanup, String resultsFolderPath, String logFilePath, Int32 totalBenchmarkCount, StartedClock& runsChronometer, Int32& benchmarksToRunCount, TaskbarProgress taskbarProgress)
   at BenchmarkDotNet.Running.BenchmarkRunnerClean.Run(BenchmarkRunInfo[] benchmarkRunInfos)
   at BenchmarkDotNet.Running.BenchmarkSwitcher.RunWithDirtyAssemblyResolveHelper(String[] args, IConfig config, Boolean askUserForInput)
   at BenchmarkDotNet.Running.BenchmarkSwitcher.Run(String[] args, IConfig config)
   at JsonBenchmark.Program.Main(String[] args) in C:\Users\Ricardo.Peres\source\repos\JsonBenchmark\JsonBenchmark\Program.cs:line 7

The exception is: "System.InvalidOperationException: TypeInfoResolver 'JsonBenchmark.DataSourceGenerationContext' did not provide property metadata for type 'JsonBenchmark.Data'."

When I try to serialize/deserialize a simple class:

public class Data
{
    public int Id { get; set; }
    public required string Name { get; set; }
    public DateTime Timestamp { get; set; }
}

Using a source generator (System.Text.Json):

[JsonSerializable(typeof(Data), GenerationMode = JsonSourceGenerationMode.Serialization)]
internal partial class DataSourceGenerationContext : JsonSerializerContext { }

The code itself runs when not running as a benchmark:

var data = new Data { Id = 1, Name = ".NET Serialization Benchmark", Timestamp = DateTime.Now };
var json = "{\"Id\":1,\"Name\":\"Ricardo\",\"Timestamp\":\"2025-02-17T13:45:53.2140627+00:00\"}";
System.Text.Json.JsonSerializer.Serialize(data);
System.Text.Json.JsonSerializer.Deserialize<Data>(json);
System.Text.Json.JsonSerializer.Serialize(data, DataSourceGenerationContext.Default.Data);
System.Text.Json.JsonSerializer.Deserialize(json, DataSourceGenerationContext.Default.Data);

But fails when I run it as part of a benchmark using BenchmarkDotNet:

public static void Main(string[] args) => BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);

The failing code is either of: JsonSerializer.Serialize(data); JsonSerializer.Serialize(data, typeof(Data), DataSourceGenerationContext.Default);

According to all documentation I read, this should work, and, in fact, it does, just not as a benchmark. I am using .NET 9.

If I have, on my project (.csproj) file this:

<PropertyGroup>  
    <JsonSerializerIsReflectionEnabledByDefault>true</JsonSerializerIsReflectionEnabledByDefault>
</PropertyGroup>

Buf it I take it out, the result is the same. The code that fails is:

JsonSerializer.Deserialize(json, DataSourceGenerationContext.Default.Data);

It says that the DataSourceGenerationContext class does not provide metadata for type Data, which seems weird, because it should be generated by the source generator.

If, however, I set the GenerationMode property to JsonSourceGenerationMode.Metadata, on the JsonSerializableAttribute it works. But I wanted to use JsonSourceGenerationMode.Serialization because of the performance increase.

Upvotes: 1

Views: 29

Answers (1)

Ricardo Peres
Ricardo Peres

Reputation: 14555

I think I found out: as per this ticket, deserialization using JsonSourceGenerationMode.Serialization is not yet implemented! The alternative is to stick with the default.

Upvotes: 1

Related Questions