Xml Polymorphism

In my scenario a policy XML holds a collection of Risk elements. The problem is that each Risk is actually a different type depending on the RiskCode. Each type has different properties, etc. I wanted to use attribute based deserialization without writing a custom parser so I decided to transform the risk element based on the RiskCode.

Here is my approach.

  1. Use Linq To Xml to transform the input document
    • Change the Risk element name based on RiskCode
  2. Use attribute based deserialization
    • All risk nodes map to a class that inherits Risk. Risk contains elements that are common to all classes.

I like this because I think the Linq to Xml transform is much simpler to understand compared to XSLT and the attribute based deserialization should be easier to maintain. We can standardize risk behavior by adding interfaces or virtual methods to the base class.

Input XML

<Policy>
  <PolicyNumber>123</PolicyNumber>
  <Risks>
    <Risk>
      <RiskCode>FOO</RiskCode>
      <Name>Blueberry</Name>
    </Risk>
    <Risk>
      <RiskCode>BAR</RiskCode>
      <Number>2</Number>
    </Risk>
  </Risks>
</Policy>

Transforms To

<Policy>
  <PolicyNumber>123</PolicyNumber>
  <Risks>
    <FooRisk>
      <RiskCode>FOO</RiskCode>
      <Name>Blueberry</Name>
    </FooRisk>
    <BarRisk>
      <RiskCode>BAR</RiskCode>
      <Number>2</Number>
    </BarRisk>
  </Risks>
</Policy>

Class Model

public class Policy
{
    [XmlElement("PolicyNumber")]
    public string PolicyNumber { get; set; }
 
    [XmlArray("Risks")]
    [XmlArrayItem("FooRisk", typeof(FooRisk))]
    [XmlArrayItem("BarRisk", typeof(BarRisk))]
    public Risk[] Risks { get; set; }
}
 
public abstract class Risk
{
    [XmlElement("RiskCode")]
    public string RiskCode { get; set; }
}
 
public class FooRisk : Risk
{
    [XmlElement("Name")]
    public string Name { get; set; }
}
 
public class BarRisk : Risk
{
    [XmlElement("Number")]
    public int Number { get; set; }
}

Transform

public static string TransformXml(string input){       
    var doc = XDocument.Parse(input);
 
    foreach (var risk in doc.Descendants("Risk"))
    {
        switch (risk.Element("RiskCode").Value)
        {
            case "FOO":
                risk.Name = "FooRisk";
                break;
            case "BAR":
                risk.Name = "BarRisk";
                break;
        }
    }
    return doc.ToString();
}

Usage Example

static void Main(string[] args)
{
    var transformed = TransformXml(xmlText);
    var policy = Deserialize<Policy>(transformed);
 
    Console.WriteLine(policy.PolicyNumber);
 
    foreach (var risk in policy.Risks)
    {
        Console.WriteLine(risk.RiskCode);
 
        if (risk is FooRisk)
        {
            var r = risk as FooRisk;
            Console.WriteLine(r.Name);
        }
        else if (risk is BarRisk)
        {
            var r = risk as BarRisk;
            Console.WriteLine(r.Number);
        }
    }            
}