I’ve got a frontend application which is producing a JSON object (let’s call it a RuleGroup
). Each RuleGroup
contains a list of either subgroups or nodes.
Here is a rough example of what this may look like:
{
"combinator": "OR", <---RuleGroup
"not": true,
"criteria": [
{
"field": "path", <---RuleCriteria
"key": "",
"value": "",
"operator": "="
},
{
"combinator": "AND", <---RuleGroup
"not": false,
"criteria": [
{
"field": "header", <---Node
"key": "",
"value": "",
"operator": "="
},
]
}
]
}
How can I deserialise an object like this on my API controller?
I’ve tried setting up some types like this however my criteria list ends up full of BaseRule
objects that cannot be cast to the other two types.
public class RuleGroup : BaseRule
{
public string Combinator { get; set; }
public bool Not { get; set; }
public List<BaseRule> Criteria { get; set; }
}
public class RuleCriteria : BaseRule
{
public string Field { get; set; }
public string Operator { get; set; }
public string Key { get; set; }
public string Value { get; set; }
}
public class BaseRule
{
}
I suspect what I need is something like https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/polymorphism?pivots=dotnet-8-0#polymorphic-type-discriminators however I don’t really want to add a dedicated type field to my data.
My last option is to manually deserialise the data back into the appropriate objects.
If there’s a way to do this without me rolling my own deserialiser, please let me know.
Thanks
2
Answers
I used https://json2csharp.com/ to see what it will generate based on your example and I received:
In my opinion, it makes sense. Using this classes you will get objects. The problem is: "How to distinguish RuleGroup and RuleCriteria". To tackle this I would extend
Criterion
class:Personally I would go with "standard" polymorphic approach with extra property for type discriminator, IMO it is the cleanest one code-wise, but if it is not feasible (for example JSON is generated but external system and you can’t control it) or any other reason – rolling out a custom converter is the option I would recommend to consider. You can use presence of some marker property to determine the destination type, simple one can look like the following:
And mark the base class with it:
Full demo @dotnetfiddle