Lasse V. Karlsen
Lasse V. Karlsen

Reputation: 391276

How to properly XML-serialize collections with an extensible object hierarchy in .NET?

Scenario:

  1. I got 4 classes, Configuration, Option, Option1 and Option2
  2. Option1 and Option2 inherit from Option
  3. Configuration has a property that is of type List<Option>, in which I intend to store objects that descend from Option
  4. Option is abstract, and will never be used to construct an actual instance.

The problem:

Here's an example LINQPad script that you can run:

void Main()
{

    var Configuration = new Configuration();
    Configuration.Options.Add(new Option1());
    Configuration.Options.Add(new Option2());

    var serializer = new XmlSerializer(typeof(Configuration), new Type[]
    {
        typeof(Option1),
        typeof(Option2),
    });
    var ns = new XmlSerializerNamespaces();
    ns.Add(string.Empty, string.Empty);

    using (var writer = new StringWriter())
    {
        serializer.Serialize(writer, Configuration, ns);
        writer.ToString().Dump();
    }
}

[XmlType("configuration")]
public class Configuration
{
    private readonly List<Option> _Options = new List<Option>();

    [XmlElement("options")]
    public List<Option> Options { get { return _Options; } }

    [XmlArray("options2")]
    [XmlArrayItem(typeof(Option1))]
    [XmlArrayItem(typeof(Option2))]
    public List<Option> Options2 { get { return _Options; } }
}

public class Option { }

[XmlType("option1")]
public class Option1 : Option { }

[XmlType("option2")]
public class Option2 : Option { }

When you run this, you get the following output:

<?xml version="1.0" encoding="utf-16"?>
<configuration>
  <options d2p1:type="option1" xmlns:d2p1="http://www.w3.org/2001/XMLSchema-instance" />
  <options d2p1:type="option2" xmlns:d2p1="http://www.w3.org/2001/XMLSchema-instance" />
  <options2>
    <option1 />
    <option2 />
  </options2>
</configuration>

Notice how the first property, options is serialized quite different from options2, but they have the same content.

Basically, I'd like to have the serialized xml look like options2, but without having to specify the allowed type of classes through attributes, since this will make extensibility impossible.

If I have to provide them through something that can be altered at runtime, that's fine, but hardcoded at compile-time is a no-go.

Upvotes: 2

Views: 291

Answers (1)

Marc Gravell
Marc Gravell

Reputation: 1062502

I think you are going to have to look at XmlAttributeOverrides, which lets you supply the attributes at runtime. This is not trivial, but not mind-melting either. The biggest catch: when you use the constructor that accepts XmlAttributeOverrides an assembly is generated every time, so be sure to cache and re-use the XmlSerializer instance - otherwise you will leak assemblies (they can't be collected).

To implement you would have an XmlAttributes instance to which you tweak XmlArray and XmlArrayItems, then use XmlAttributeOverrides.Add(Type,Member,XmlAttributes) to associate that with "Options2".

Upvotes: 2

Related Questions