I need my web-api to return a list of Rule
instances serialized in json format.
[HttpGet]
[SwaggerOperation(nameof(GetRules))]
[SwaggerResponse(StatusCodes.Status200OK, typeof(List<Rule>), "Rules")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<IActionResult> GetRules()
{
List<Rule> rules = /* retrieve rule from some storage */;
return Ok(rules);
}
For now, there are 2 kinds of rules, each with specific properties on top of the ones shared in the Rule class; one rule is called RuleWithExpiration
and the other RuleWithGracePeriod
.
[JsonObject(MemberSerialization.OptIn)]
public class Rule
{
[JsonProperty("id")]
public Guid Id { get; }
[JsonProperty("name")]
public string Name { get; }
[JsonConstructor]
public Rule(Guid id, string name)
{
Id = id;
Name = name;
}
}
[JsonObject(MemberSerialization.OptIn)]
public class RuleWithExpiration : Rule
{
[JsonProperty("someInfo")]
public string SomeInfo { get; }
[JsonProperty("expiration")]
DateTime Expiration { get; }
[JsonConstructor]
public RuleWithExpiration(Guid id, string name, string someInfo, DateTime expiration) : base(id, name)
{
SomeInfo = someInfo;
Expiration = expiration;
}
}
[JsonObject(MemberSerialization.OptIn)]
public class RuleWithGracePeriod : Rule
{
[JsonProperty("gracePeriod")]
public TimeSpan GracePeriod { get; }
[JsonConstructor]
public RuleWithGracePeriod(Guid id, string name, TimeSpan gracePeriod) : base(id, name)
{
GracePeriod = gracePeriod;
}
}
The problem I have is that this class hierarchy has issues when I try to deserialized it. After the deserialization, I end up with a list of Rule
instances since I do not ask the serializer to include the type information, as it is considered a security issue.
void Main()
{
List<Rule> rules = new List<Rule>
{
new RuleWithExpiration(Guid.NewGuid(), "Rule with expiration", "Wat?", DateTime.UtcNow.AddHours(1d)),
new RuleWithGracePeriod(Guid.NewGuid(), "Rule with grace period", TimeSpan.FromHours(1d)),
};
var serializedRule = JsonConvert.SerializeObject(rules);
serializedRule.Dump();
List<Rule> deserializedRule = JsonConvert.DeserializeObject<List<Rule>>(serializedRule);
deserializedRule.Dump();
}
Here is the serialized string:
[{"someInfo":"Wat?","expiration":"2018-07-26T13:32:06.2287669Z","id":"29fa0603-c103-4a95-b627-0097619a7645","name":"Rule with expiration"},{"gracePeriod":"01:00:00","id":"bd8777bb-c6b3-4172-916a-546775062eb1","name":"Rule with grace period"}]
And here is the list of Rule
instances I get after it is deserialized (as shown in LINQPad):
Question
Is it possible to keep this inheritance tree in this context or do I have to rearrange these classes somehow? If so, what would be the way to do this?
Solution
I have not found solutions that felt good.
For instance, I could have some
RuleAggregate
class like this one, but everytime I introduce a new kind of rule, I have to edit this class and deal with the impact:
[JsonObject(MemberSerialization.OptIn)]
public class RuleAggregate
{
[JsonProperty("expirations")]
public List<RuleWithExpiration> Expirations {get;}
[JsonProperty("gracePeriods")]
public List<RuleWithGracePeriod> GracePeriods {get;}
[JsonConstructor]
public RuleAggregate(List<RuleWithExpiration> expirations, List<RuleWithGracePeriod> gracePeriods)
{
Expirations = expirations;
GracePeriods = gracePeriods;
}
}
The solution I found with the less trade-offs -if I want to keep the inheritance tree- is to fall back on the good ol' XML serialization.