-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Fallback to configurable type for JsonPolymorphic deserialization #111755
Comments
Tagging subscribers to this area: @dotnet/area-system-text-json, @gregsdennis |
Hi @rynowak, thanks for the detailed write-up -- this is good feedback. I think it should be possible in principle to implement something that supports your use case (with a few minor tweaks to keep it compatible with the current contract model): [JsonPolymorphic(TypeDiscriminatorPropertyName = "$type", Fallback = typeof(Unknown))]
[JsonDerivedType(typeof(A), "A")]
[JsonDerivedType(typeof(B), "B")]
interface IWidget; Judging by your second example, I would infer that you need a way to bind the unrecognized discriminator id to a property of the public record Unknown : IWidget
{
[JsonExtensionData]
public IDictionary<string, object?>? AdditionalProperties { get; set; }
} which should accumulate all unbound properties (including the type discriminator). This should ensure that values can be safely roundtripped as well. Any such work should be done in conjunction with #72170. |
Thanks @eiriktsarpalis for taking a look at this.
Yes that's right. I linked #108885 because it seems relevant.
This is what I ended up doing. Do you have any feedback on the approach of writing a |
Scenario
My use case is deserializing polymorphic JSON data from an external REST API that's following the well-known discriminator pattern using
$type
. The challenge is that don't control this API and they continue to add new discriminator values.This is further complicated because we need to do custom processing on some of the types of data returned by the API. So we want to define C# types for some of the API data but not all of it.
This isn't quite an API proposal per-se because I'm looking for feedback on the approach and overall idea. If this seems like a good idea, I could turn it into that.
The code that I want to write looks something like this:
This is good because we can agnostically "pass through" any new widget types returned by the service we're calling. We only need to define new C# types whenever a specific widget type requires some business logic.
Note: This example uses an interface + record structs. I'm not sure if that's optimal from a performance POV, and it could be somewhat flexible. Unless I'm missing something, whether it's classes or structs doesn't really affect my scenario.
What I tried
I can't use
IgnoreUnrecognizedTypeDiscriminators
because I need to read all the data.Traditional inheritance isn't a good solution for this because both the child and base class need to define
details
with different types. Therefore I can't useUnknownDerivedTypeHandling
. I'm not sure it applies to deserialization anyway.I also need to round-trip the data without information loss, so I'd need something like #108885 to be addressed.
I'd really like to avoid duplicating the functionality of
[JsonPolymorphic]
by writing or generating our own serialization logic.It's also not possible to use both of
JsonConverter
andJsonPolymorphic
for the same type.Why this is valuable
As C# has added more language features for immutability and sum types (eg: records, pattern matching) we're using these patterns to great success.
STJ has really excellent support for working with a closed set of polymorphic strong types. This is about a the ability to work with an open set, or partial-typing.
I'm wondering if a lot of the other issues that have been opened on
[JsonPolymorphic]
are asking about workarounds for this scenario.An approach that works with drawbacks
I found an approach that works and I'm currently trying to shake out all of the problems with. I'd appreciate any feedback that the experts can share in case this can be improved.
This works by defining two polymorphic types:
JsonConverter
JsonPolymorphic
The job of the converter is to detect whether or not the discriminator value is part of the 'known' set. I can do this by using
JsonTypeInfo<>
so I have a single source of truth. Then it calls into the serialize for eitherIStronglyTypedWidget
orUnknown
.I think the drawbacks of this are:
I'm currently working on a benchmark for the approach below in comparison with static approaches like hand-writing the serializer. Happy to share the results if it's interesting. I remember from the design discussions that converters introduce buffering, and that's the part that most concerns me.
The text was updated successfully, but these errors were encountered: