-
Notifications
You must be signed in to change notification settings - Fork 5.1k
Fix JsonSerializerOptions.GetConverter recreating built-in converters #65799
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -99,7 +99,6 @@ private void EnsurePushCapacity() | |
public JsonConverter Initialize(Type type, JsonSerializerOptions options, bool supportContinuation) | ||
{ | ||
JsonTypeInfo jsonTypeInfo = options.GetOrAddJsonTypeInfoForRootType(type); | ||
Debug.Assert(options == jsonTypeInfo.Options); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This assertion has been invalidated by #64646, however it wasn't being caught by our test suite until now. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I assume it would still be true if the new comparer introduced in #64646 were used. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
return Initialize(jsonTypeInfo, supportContinuation); | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,8 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System.IO; | ||
using Microsoft.DotNet.RemoteExecutor; | ||
using Xunit; | ||
|
||
namespace System.Text.Json.Serialization.Tests | ||
|
@@ -161,19 +163,29 @@ public static void VerifyObjectConverterWithPreservedReferences() | |
Assert.Equal(true, obj); | ||
} | ||
|
||
[Fact] | ||
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] | ||
public static void GetConverterRootsBuiltInConverters() | ||
{ | ||
JsonSerializerOptions options = new(); | ||
RunTest<DateTime>(); | ||
RunTest<Point_2D>(); | ||
|
||
void RunTest<TConverterReturn>() | ||
RemoteExecutor.Invoke(static () => | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this feels a bit like a unit test rather than functional test because it tests an implementation detail. I'll suggest to move it to unit tests. |
||
{ | ||
JsonConverter converter = options.GetConverter(typeof(TConverterReturn)); | ||
Assert.NotNull(converter); | ||
Assert.True(converter is JsonConverter<TConverterReturn>); | ||
} | ||
JsonSerializerOptions options = new(); | ||
RunTest<DateTime>(); | ||
RunTest<Point_2D>(); | ||
|
||
void RunTest<T>() where T : new() | ||
{ | ||
JsonConverter<T> converter = options.GetConverter(typeof(T)) as JsonConverter<T>; | ||
Assert.NotNull(converter); | ||
|
||
// Should be possible to call into converters that require | ||
// reflection-based metadata | ||
T value = new(); | ||
using var writer = new Utf8JsonWriter(new MemoryStream()); | ||
converter.Write(writer, value, options); | ||
writer.Flush(); | ||
Assert.True(writer.BytesCommitted > 0); | ||
} | ||
}).Dispose(); | ||
} | ||
|
||
[Fact] | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think calling this method should root the reflection-based metadata gathering code paths, potentially masking future bugs with source-gen.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. What is the distinction between rooting the reflection-based metadata gathering paths and rooting the built-in converter factories that depend on said reflection-based metadata gathering paths? Under the current setup an options instance not configured for reflection will eagerly return converters that do require reflection.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Based on your remarks on this PR #51897 I'm guessing that this is only meant as an entrypoint for reflection-based serialization anyways.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm what do you mean? The source generator doesn't use this method
GetConverter(Type)
, so we left the reflection to avoid breaking folks that used it pre .NET 6.0.