C
C#2y ago
ascpixi

❔ ✅ Deserializing a JSON array that contains a derived type AND a base type with JSON.NET?

I have three classes:
ScratchTarget
├─ ScratchScene
└─ ScratchSprite
ScratchTarget
├─ ScratchScene
└─ ScratchSprite
ScratchTarget contains all common fields that ScratchScene and ScratchSprite will have; I know that all of the JSON array entries will have fields from that class. However, some objects will be of type ScratchScene, and some will be of type ScratchSprite. I have a isStage field in ScratchTarget that informs me of the type of said ScratchTarget. How would I go about deserializing such a JSON array to a List<ScratchTarget>, with the JSON.NET deserializer also converting the extra fields by picking either ScratchScene or ScratchSprite?
12 Replies
Amos
Amos2y ago
Can you give us an example of the Json data you'd be putting in? For me, it's a little easier to visualise the mapping. 🙂
Accord
Accord2y ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.
ascpixi
ascpixi2y ago
hi, sorry for the late response, the data looks like this:
{
"targets": [
{
"isStage":true,
"name":"Stage",
"videoTransparency":50,
[...]
},
{
"isStage":false,
"name":"Sprite1",
"draggable":false,
"rotationStyle":"all around",
[...]
}
],
}
{
"targets": [
{
"isStage":true,
"name":"Stage",
"videoTransparency":50,
[...]
},
{
"isStage":false,
"name":"Sprite1",
"draggable":false,
"rotationStyle":"all around",
[...]
}
],
}
the ScratchScene object has "isStage" set to true, and has a videoTransparency key, which ScratchSprite lacks; the ScratchSprite object has "isStage" set to false and has draggable and rotationStyle there shouldn't be any instances of just the base type, ScratchTarget, so no need to worry about that
Amos
Amos2y ago
Honestly, I have absolutely no idea. I'm finding the initial question kinda difficult to understand though. My first thought is why not just have all the relevant properties of "targets" in its own class, and where the fields aren't needed then null them. Doesn't matter if the property is populated if it's not read in the context of the property not being needed. If you're struggling to find a nice way to do this with properties, it's likely the structure itself is wrong. That's what I've been told before at least. 🙂 @ascpixi
ascpixi
ascpixi2y ago
unfortunately i can't change the structure because it's an external one i have to parse ;( i could just make one class and have stuff be null when they're not applicable but that doesn't seem like good design while it is like that in the original format i'd like to make it have a better representation in my C# code
Amos
Amos2y ago
If there's duplicate elements you could have those as an inherited class. Then you could just work all the way down until there are unique elements in each class and it works perfect. But the class abstraction for this would be really high and kinda messy IMO. Or you could do it with fewer classes and less abstraction but having duplicate properties. Probably the best idea. - I think what you're doing? Or you could have one mega class with all the properties and null the ones that aren't used. This is a little messy, but should actually be fine if you only read the ones you care for. There is likely a better solution to this I just do not know of yet. Maybe someone else chips in with their help. 🤷‍♂️
ascpixi
ascpixi2y ago
yes, that's ScratchProject - the class structure is already written down in C#
ascpixi
ascpixi2y ago
this is the structure and ScratchTarget contains all common properties
Amos
Amos2y ago
I think you've arrived at the best solution for now until someone comes in with a better one. 😄
ascpixi
ascpixi2y ago
i figured it out! there is a (pretty popular, 21mill downloads) NuGet package called JsonSubtypes that allows you to do this:
[Serializable]
[JsonConverter(typeof(JsonSubtypes), nameof(Mode))]
[JsonSubtypes.KnownSubType(typeof(ScratchListMonitor), ScratchMonitorMode.List)]
[JsonSubtypes.FallBackSubType(typeof(ScratchValueMonitor))]
public abstract class ScratchMonitor : ScratchObject
[Serializable]
[JsonConverter(typeof(JsonSubtypes), nameof(Mode))]
[JsonSubtypes.KnownSubType(typeof(ScratchListMonitor), ScratchMonitorMode.List)]
[JsonSubtypes.FallBackSubType(typeof(ScratchValueMonitor))]
public abstract class ScratchMonitor : ScratchObject
this is a different example but it works the same if Mode is equal to ScratchMonitorMode.List, it serializes it to a ScratchListMonitor; else, to a ScratchValueMonitor
Amos
Amos2y ago
Ah nicely done! 🙂
Accord
Accord2y ago
Closed! Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.