XmlSerialize a custom collection with an Attribute

2019-01-11 13:12发布

I've got a simple class that inherits from Collection and adds a couple of properties. I need to serialize this class to XML, but the XMLSerializer ignores my additional properties.

I assume this is because of the special treatment that XMLSerializer gives ICollection and IEnumerable objects. What's the best way around this?

Here's some sample code:

using System.Collections.ObjectModel;
using System.IO;
using System.Xml.Serialization;

namespace SerialiseCollection
{
    class Program
    {
        static void Main(string[] args)
        {
            var c = new MyCollection();
            c.Add("Hello");
            c.Add("Goodbye");

            var serializer = new XmlSerializer(typeof(MyCollection));
            using (var writer = new StreamWriter("test.xml"))
                serializer.Serialize(writer, c);
        }
    }

    [XmlRoot("MyCollection")]
    public class MyCollection : Collection<string>
    {
        [XmlAttribute()]
        public string MyAttribute { get; set; }

        public MyCollection()
        {
            this.MyAttribute = "SerializeThis";
        }
    }
}

This outputs the following XML (note MyAttribute is missing in the MyCollection element):

<?xml version="1.0" encoding="utf-8"?>
<MyCollection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <string>Hello</string>
    <string>Goodbye</string>
</MyCollection>

What I want is

<MyCollection MyAttribute="SerializeThis" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <string>Hello</string>
    <string>Goodbye</string>
</MyCollection>

Any ideas? The simpler the better. Thanks.

5条回答
We Are One
2楼-- · 2019-01-11 13:48

I've been fighting with the same issue Romaroo is (wanting to add properties to the xml serialization of a class that implements ICollection). I have not found any way to expose properties that are in the collection class. I even tried using the XmlAttribute tag and making my properties show up as attributes of the root node, but no luck there either. I was however able to use the XmlRoot tag on my class to rename it from "ArrayOf...". Here are some references in case you're interested:

查看更多
劳资没心,怎么记你
3楼-- · 2019-01-11 13:51

Collections generally don't make good places for extra properties. Both during serialization and in data-binding, they will be ignored if the item looks like a collection (IList, IEnumerable, etc - depending on the scenario).

If it was me, I would encapsulate the collection - i.e.

[Serializable]
public class MyCollectionWrapper {
    [XmlAttribute]
    public string SomeProp {get;set;} // custom props etc
    [XmlAttribute]
    public int SomeOtherProp {get;set;} // custom props etc
    public Collection<string> Items {get;set;} // the items
}

The other option is to implement IXmlSerializable (quite a lot of work), but that still won't work for data-binding etc. Basically, this isn't the expected usage.

查看更多
淡お忘
4楼-- · 2019-01-11 14:02

Sometimes you just want to do what you just want to do; the Framework be damned.

I posted an answer here Property of a List<T> Not Deserialized

that does what the OP wanted to do.

查看更多
我想做一个坏孩纸
5楼-- · 2019-01-11 14:09

If you do encapsulate, as Marc Gravell suggests, the beginning of this post explains how to get your XML to look exactly like you describe.

http://blogs.msdn.com/youssefm/archive/2009/06/12/customizing-the-xml-for-collections-with-xmlserializer-and-datacontractserializer.aspx

That is, instead of this:

<MyCollection MyAttribute="SerializeThis" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Items>
    <string>Hello</string>
    <string>Goodbye</string>
  <Items>
</MyCollection>

You can have this:

<MyCollection MyAttribute="SerializeThis" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns:xsd="http://www.w3.org/2001/XMLSchema">      
  <string>Hello</string>
  <string>Goodbye</string>
</MyCollection>
查看更多
家丑人穷心不美
6楼-- · 2019-01-11 14:13

As Neil Whitaker suggests and in case his link dies..

Create an inner collection to store the strings and apply the XmlElement attribute to mask the collection name. Produces the same xml output as if MyCollection inherited from Collection, but also serializes attributes on the parent element.

[XmlRoot("MyCollection")]
public class MyCollection 
{
    [XmlAttribute()]
    public string MyAttribute { get; set; }

    [XmlElement("string")]
    public Collection<string> unserializedCollectionName { get; set; }

    public MyCollection()
    {
        this.MyAttribute = "SerializeThis";
        this.unserializedCollectionName = new Collection<string>();
        this.unserializedCollectionName.Add("Hello");
        this.unserializedCollectionName.Add("Goodbye");
    }
}
查看更多
登录 后发表回答