-
Notifications
You must be signed in to change notification settings - Fork 4.9k
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Provide trim/AOT-safe mechanism to (de)serialize enums from strings with JSON source-gen #79311
Comments
Tagging subscribers to this area: @dotnet/area-system-text-json, @gregsdennis Issue DetailsForking from #73124 which goes over a friendly, general pattern for This issue focuses on a plan to support
|
Curious about this since we're also deserializing a bunch of enums from strings in the Store. The way we're currently doing this (to keep backwards compatibility with Newtonsoft.Json) is by having a custom [JsonConverter(typeof(ResilientStringEnumConverter<SomeEnum>))]
enum SomeEnum
{
A,
B,
C
} This works just fine including with trimming (we're using .NET Native and it works with no issues as far as we can tell), and by inspecting the generated code we can see that in fact the generator is creating an instance of each specialized converter for each property of one of our enum types. So just to understand this better, is the goal of this issue to add support for this in a way that it also works when eg. passed via a non-generic context (eg. via the options) or when annotating an enum type with the non-generic converter type? As in, it seems like this is otherwise already supported and working fine with trimming on AOT as long as you annotate each enum type with the correct generic converter already? 🤔 Just trying to get a better understanding on this 🙂 |
The goal is to have usage of |
Just to elaborate on what I meant - it seems to me there's two different issues with respect to enum serialization, which don't necessarily need the same solution, as it doesn't have the same complexity. What I meant is that:
For point 2, that is, like I mentioned in my previous example, if you have: [JsonConverter(typeof(JsonStringEnumConverter<MyEnum>))]
public enum MyEnum { A, B } Then the source generator will just generate code to construct a new To clarify - I'm not saying that a general factory pattern isn't needed, but just that in case it might be useful to break this work down into smaller units, the second part can be made to work without the need for any new APIs/patterns, is all 🙂 |
I've updated the OP to include the new API necessary to make string enum serialization work. |
I don't think we could solve this without any new APIs. The underlying converter implementation, |
Yup! To clarify, I didn't mean "no public API changes at all", I meant that making the existing |
namespace System.Text.Json.Serialization.Metadata;
public static class JsonMetadataServices
{
// Existing:
// public static JsonConverter<T> GetEnumConverter<T>(JsonSerializerOptions options) where T : struct, Enum;
public static JsonConverter<T> GetStringEnumConverter<T>(JsonStringEnumConverter converterFactory, JsonSerializerOptions options) where T : struct, Enum
} |
FYI - I ran into this issue trying to make a piece of YARP AOT-compatible: dotnet/yarp#2145. |
Per discussion in #87149 (comment) it looks like using the existing API proposalnamespace System.Text.Json.Serialization;
[RequiresDynamicCode]
public partial class JsonStringEnumConverter : JsonConverterFactory
{
public JsonStringEnumConverter();
public JsonStringEnumConverter(JsonNamingPolicy? namingPolicy = null, bool allowIntegerValues = true);
public override bool CanConvert(Type type) => type.IsEnum;
}
+public class JsonStringEnumConverter<TEnum> : JsonConverterFactory
+ where TEnum : struct, Enum
+{
+ public JsonStringEnumConverter();
+ public JsonStringEnumConverter(JsonNamingPolicy? namingPolicy = null, bool allowIntegerValues = true);
+
+ public override bool CanConvert(Type type) => type == typeof(TEnum);
+} API Usagepublic class MyPoco
{
[JsonConverter(typeof(JsonStringEnumConverter<BindingFlags>))]
public BindingFlags Flags { get; set; }
} We should also include a diagnostic in the source generator that prompts users to replace instances of the old |
namespace System.Text.Json.Serialization;
// Existing:
//
// [RequiresDynamicCode]
// public partial class JsonStringEnumConverter : JsonConverterFactory
// {
// public JsonStringEnumConverter();
// public JsonStringEnumConverter(JsonNamingPolicy? namingPolicy = null, bool allowIntegerValues = true);
//
// public override bool CanConvert(Type type) => type.IsEnum;
// }
public class JsonStringEnumConverter<TEnum> : JsonConverterFactory
where TEnum : struct, Enum
{
public JsonStringEnumConverter();
public JsonStringEnumConverter(JsonNamingPolicy? namingPolicy = null, bool allowIntegerValues = true);
public override bool CanConvert(Type type) => type == typeof(TEnum);
} |
See #79311 (comment) for an updated API proposal.
Original Proposal
Background and Motivation
The
JsonStringEnumConverter
class is a built-inJsonConverterFactory
that is not compatible with NativeAOT, since it relies onType.MakeGenericType()
to work. We need to update the source generator so that it intercepts[JsonConverter(typeof(JsonStringEnumConverter))]
annotations and replaces them with a NativeAOT-safe factory method invocation.Should be addressed in conjunction with #81833.
API Proposal
namespace System.Text.Json.Serialization.Metadata; public static class JsonMetadataServices { public static JsonConverter<T> GetEnumConverter<T>(JsonSerializerOptions options) where T : struct, Enum + public static JsonConverter<T> GetStringEnumConverter<T>(JsonStringEnumConverter converterFactory, JsonSerializerOptions options) where T : struct, Enum }
Alternative Design
We could add the generic factory method on the converter factory itself:
public class JsonStringEnumConverter { public JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options); + public JsonConverter<T> CreateConverter<T>(JsonSerializerOptions options) where T : struct, Enum }
However I would prefer if this call were hidden behind the
EditorBrowsable.Never
JsonMetadataServices class.Edited by @eiriktsarpalis
Original Proposal
Forking from https://github.com//issues/73124 which goes over a friendly, general pattern for JsonConverterFactory usage with source-gen.This issue focuses on a plan to support [JsonStringEnumConverter] (or an alternate approach), the primary scenario needed in 8.0 for the ASP.NET effort for AOT friendlinessfriendliness (JSON items tracked by #79122).
The text was updated successfully, but these errors were encountered: