C
C#7mo ago
Kiel

Deserializing 3rd-party JSON into a base (abstract) class in System.Text.Json

What is the most performant way to accomplish this? The most common solution to this problem (in Json.NET anyway?) is to implement a custom JsonConverter which first converts the object to a JObject, then checks for a discriminator property (or properties) before deciding how to deserialize/convert the JObject. This works, clearly, but I have doubts/concerns about its performance at scale as far as speed is concerned. I really, REALLY would like to avoid anything involving shoving every single possible field from the derived classes into one massive model abomination. Changing the JSON is out of the question as this is third-party data coming in from a websocket. The data is pretty much
{
"type": "SomeType",
...
}
{
"type": "SomeType",
...
}
followed by SomeType-specific event fields. type is guaranteed to exist. Could I use this field as a discriminator somehow? My concern is that solutions involving JsonConverter delve into solutions almost always including a switch statement on the discriminator property. My issue is that this type field can have...almost 35 values, and I'm sure even more as the events this websocket can send to me will expand with time as well. The idea of a 35-case switch statement doesn't sit well with me
2 Replies
Kiel
Kiel7mo ago
And no, I haven't actually tried benchmarking the Json.NET approach (or whatever the STJ equivalent would be), this is obviously just a gut feeling, so if I'm totally wrong about how slow doing it that way is, I'm open to considering it anyways. I have been enlightened as to another way to accomplish this, and I can confirm it works:
[JsonPolymorphic(TypeDiscriminatorPropertyName = "type")]
[JsonDerivedType(typeof(AuthenticatedEventApiModel), nameof(BonfireEventType.Authenticated))]
// like 30 more of these
[JsonDerivedType(typeof(UserUpdateEventApiModel), nameof(BonfireEventType.UserUpdate))]
public abstract record IncomingEventApiModel : ApiModel
{
[JsonPropertyName("type")]
public BonfireEventType Type { get; init; }
}
[JsonPolymorphic(TypeDiscriminatorPropertyName = "type")]
[JsonDerivedType(typeof(AuthenticatedEventApiModel), nameof(BonfireEventType.Authenticated))]
// like 30 more of these
[JsonDerivedType(typeof(UserUpdateEventApiModel), nameof(BonfireEventType.UserUpdate))]
public abstract record IncomingEventApiModel : ApiModel
{
[JsonPropertyName("type")]
public BonfireEventType Type { get; init; }
}
I'm actually OK with this, but one issue...Any idea why Type never seems to be deserialized correctly? No matter what the model type, Type is always Error, which is the default (first) value defined in the enum. This is the case even when I've confirmed that my serializer settings are using a JsonStringEnumConverter. Do I need to do something special for it to work? it's weird that it's deserializing to the correct type, meaning SOMETHING is picking up the right value, but then it's not getting deserialized correctly.
Joschi
Joschi7mo ago
Not sure it makes it better or more performant (it's probably worse?), but it saved me a lot of headaches once. You can actually transform the xml using xslt at runtime and replace the original node name with the value of your discriminator. Then you deserialize it all like normal nodes.