1

I'm trying to constrol where a class property is rendered when the class is serialized: I need the property to appear as an attribute on a specific element:

namespace ConsoleApplication6
{
    public class Program
    {
        static void Main(string[] args)
        {
            var myClass = new MyClass();
            myClass.MyList.Add(new Item() { ID = 1 });
            myClass.MyList.Add(new Item() { ID = 2 });
            myClass.Xxx = "Hello World!";

            var sx = new XmlSerializer(myClass.GetType());
            sx.Serialize(Console.Out, myClass);
        }

        public class MyClass
        {
            public MyClass()
            {
                MyList = new List<Item>();
            }

            public List<Item> MyList { get; set; }

            [XmlAttributeAttribute(AttributeName = "x")]
            public string Xxx { get; set; }
        }

        public class Item
        {
            public int ID { get; set; }
        }
    }
}

This serializes quite nicely into this:

<?xml version="1.0" encoding="ibm850"?>
<MyClass xmlns:xsi=" ... " xmlns:xsd=" ... " x="Hello World!">
  <MyList>
    <Item>
      <ID>1</ID>
    </Item>
    <Item>
      <ID>2</ID>
    </Item>
  </MyList>
</MyClass>

BUT: My problem is, I need the property Xxx to be rendered as an attribute on the <MyList> element rather than the <MyClass> root element, like this:

...
<MyList x="Hello World!">
...

I'm GUESSING this should be possible using XmlSerialization attributes on the class/properties, but I can't figure it out. I even tried creating a subclass of List adding the property Xxx to that, but the .NET XML serialization ignores the extra properties, and the XML output is just like the List<..> is normally serialized.

Update: Here's the code where I try to create a "custom list", that inherits from List<Item> and adds an extra property:

public class Program
{
    static void Main(string[] args)
    {
        var myClass = new MyClass();
        myClass.MyList.Add(new Item() { ID = 1 });
        myClass.MyList.Add(new Item() { ID = 2 });
        myClass.MyList.Xxx = "Hello World!";

        var sx = new XmlSerializer(myClass.GetType());
        sx.Serialize(Console.Out, myClass);
    }

    public class MyClass
    {
        public MyClass()
        {
            MyList = new CustomList();
        }

        public CustomList MyList { get; set; }
    }

    public class Item
    {
        public int ID { get; set; }
    }

    public class CustomList : List<Item>
    {
        [XmlAttributeAttribute(AttributeName = "x")]
        public string Xxx { get; set; }
    }
}

The output xml looks like this:

<?xml version="1.0" encoding="ibm850"?>
<MyClass xmlns:xsi=" ... " xmlns:xsd=" ... ">
  <MyList>
    <Item>
      <ID>1</ID>
    </Item>
    <Item>
      <ID>2</ID>
    </Item>
  </MyList>
</MyClass>

Notice how the Xxx property is not represented in the xml...

Jakob Gade
  • 12,319
  • 15
  • 70
  • 118
  • 1
    Can you post the code from your attempt at adding the property to a child of the `List<>` type? The XML you say you're expecting does not match the underlying object model you've provided so of course it won't serialize as such. – M.Babcock Jan 23 '12 at 04:24
  • I've updated the question with another code sample. – Jakob Gade Jan 23 '12 at 04:34
  • Thanks for posting the code. I did some tests of my own after posting my comment. See my answer below from what I found after not getting it to work either. – M.Babcock Jan 23 '12 at 04:35

3 Answers3

1

I think for that level of control you will need to look at using the IXmlSerializable interface. I don't think using attributes will work here.

Kai G
  • 3,371
  • 3
  • 26
  • 30
0

According to this MSDN discussion:

XmlSerializer does not serialize any members if a collection. Only collection items get serialized. This is by design, basically a decision was made to handle collections as arrays not as classes with multiple properties, so collections should look like arrays on the wire, therefore they do not have any members other then collection items, and can be “flattened” by adding the [XmlElement] to the member of the ICollection type.

So apparently the flaw you describe is by design. Unless you decide to run the resulting XML through some transforms, I'm not sure how you plan to get this to work using the XML serialization attributes.

M.Babcock
  • 18,753
  • 6
  • 54
  • 84
  • Thanks, that's some interesting insight - and there's a nice code sample too, which I might use. I think the best option is simply to implement IXmlSerializable. – Jakob Gade Jan 23 '12 at 04:46
  • @JakobGade - If you've chosen an answer as your *answer* please be sure to mark it as such. – M.Babcock Jan 24 '12 at 04:28
0

This post provides some additional information, including a few options: When a class is inherited from List<>, XmlSerializer doesn't serialize other attributes

Summary:

  • IXmlSerializable (as mentioned by Kai)
  • DataContractSerializer
  • Create a new class where X (your attribute) is a property and provide an additional property that is a list (so instead of subclassing list, just create a wrapper class). For example:

    public class MyListWrapper<T> 
    {
       public MyListWrapper()
       {
          Data = new List<T>();
       }
    
       [XmlAttribute(AttributeName="x")]
       public string Xxx { get; set; }
    
       [XmlElement]
       public List<T> Data { get; set; }
    }
    

    Note that this will output Items as "Data" elements. If you're willing to drop the generic type parameter on Data and make it List(Item) then you can get items.

Hope that helps!

Community
  • 1
  • 1
remarkrm
  • 159
  • 2
  • +1 This solution comes pretty close to what the OP requires, the downside being an extra node in the XML. Still, I would prefer to implement IXmlSerializable. – Abbas Jan 23 '12 at 06:41
  • Actually, with the use of the XmlElement attribute on the Data property, you won't end up with an extra Data node. What you will end up with are items being displayed as "Data" elements (in place of "Item" elements), as described following the code sample. If you eliminate the XmlElement attribute on Data then you end up with an extra Data node wrapping Items. – remarkrm Jan 24 '12 at 15:21