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
Unseal JsonStringEnumConverter #30486
Comments
cc @steveharter, @JeremyKuhne - why was this type sealed? Is the scenario with enum flags not meant to be supported via |
This was done for performance (and future implementation flexibility). We changed the APIs explicitly so you can compose it if you want to for this exact scenario. dotnet/corefx#39438 There is a specific example in the tests that shows you how to compose. |
Thanks @JeremyKuhne, since there is a viable workaround, marking this as future. @josundt, does composition like this work for your scenario (rather than inheritance)? |
Seems to be good enough for my requirements. public class NoFlagsStringEnumConverter : JsonConverterFactory
{
private JsonStringEnumConverter _stringEnumConverter;
public NoFlagsStringEnumConverter()
: this(namingPolicy: null, allowIntegerValues: true)
{
// An empty constructor is needed for construction via attributes
}
public NoFlagsStringEnumConverter(JsonNamingPolicy namingPolicy = null, bool allowIntegerValues = true)
{
this._stringEnumConverter = new JsonStringEnumConverter(namingPolicy, allowIntegerValues);
}
public override bool CanConvert(Type typeToConvert)
=> typeToConvert.IsEnum && !typeToConvert.IsDefined(typeof(FlagsAttribute), inherit: false);
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
=> s_stringEnumConverter.CreateConverter(typeToConvert, options);
} |
That would be fine. |
Closing - there's a workaround. |
Reopening following conversation from #54187. |
How so? The public type itself is a converter factory so any perf impact would probably only impact the initial converter generation. |
It was a general API best practice. Virtuals make it harder to change internal implementation details- when something can't be derived we can make assumptions and/or take shortcuts we can't otherwise do when we're sealed. Your factory point is valid, of course, and the team can certainly decide to unseal this if it is felt to give significant value. |
Good point about changing virtuals; I have updated the OP so that virtual members are explicitly sealed. |
Since the motivating scenario behind unsealing this type is to effectively pass state through it's constructor when using |
namespace System.Text.Json.Serialization
{
// Unsealed
public class JsonStringEnumConverter : JsonConverterFactory
{
// Existing:
// public JsonStringEnumConverter();
// public JsonStringEnumConverter(JsonNamingPolicy? namingPolicy = null, bool allowIntegerValues = true);
// Existing methods, now sealed
public sealed override bool CanConvert(Type typeToConvert);
public sealed override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options);
}
} |
Background and Motivation
JsonStringEnumConverter
can be configured using a couple of parameters. But since the type is marked sealed, it can be awkward to configure when applied via theJsonConverter
attribute, leading to awkward workarounds like this one.API Proposal
Risks
Unsealing public types is generally not considered a breaking change. It might break reflection in the hypothetical scenario where a user depends on the result of
typeof(JsonStringEnumConverter).IsSealed
.Original proposal by @josundt (click to view)
In our APIs, we serialize most enums as strings (no case conversion) since string enums make APIs more self-explanatory, but for **flag enums** we serialize with the numeric value since strings would prevent bitwise flag enum operations.With NewtonSoft.Json, we accomplished the described serialization behavior by creating our own converter that inherited from NewtonSoft's
StringEnumConverter
.This would have been possible and even easier with System.Text.Json, if only the
JsonStringEnumConverter
class was not sealed.If it was not sealed, I could have done this simply by overriding the
CanConvert
method like below:Can you please make this extensibility point available ("un-sealing" the class)?
The text was updated successfully, but these errors were encountered: