How do I give an array an attribute during serialization in C#?
Asked Answered
A

3

16

I'm trying to generate C# that creates a fragment of XML like this.

<device_list type="list">
    <item type="MAC">11:22:33:44:55:66:77:88</item>
    <item type="MAC">11:22:33:44:55:66:77:89</item>
    <item type="MAC">11:22:33:44:55:66:77:8A</item>
</device_list>

I was thinking of using something like this:

[XmlArray( "device_list" ), XmlArrayItem("item")]
public ListItem[] device_list { get; set; }

as the property, with this class declaration:

public class ListItem {
    [XmlAttribute]
    public string type { get; set; }

    [XmlText]
    public string Value { get; set; }
}

Which gives me the inner serialization, but I don't know how to apply the type="list" attribute to the device_list above.

I'm thinking (but not sure of how to write the syntax) that I need to do a:

public class DeviceList {
    [XmlAttribute]
    public string type { get; set; }

    [XmlArray]
    public ListItem[] .... This is where I get lost
}

Updated based on Dave's responses

public class DeviceList : List<ListItem> {
    [XmlAttribute]
    public string type { get; set; }
}

public class ListItem {
    [XmlAttribute]
    public string type { get; set; }

    [XmlText]
    public string Value { get; set; }
}

and the usage is currently:

[XmlArray( "device_list" ), XmlArrayItem("item")]
public DeviceList device_list { get; set; }

And the type, while being declared in the code like thus:

device_list = new DeviceList{type = "list"}
device_list.Add( new ListItem { type = "MAC", Value = "1234566" } );
device_list.Add( new ListItem { type = "MAC", Value = "1234566" } );

Isn't showing the type on serialization. This is the result of the serialization:

<device_list>
  <item type="MAC">1234566</item>
  <item type="MAC">1234566</item>
</device_list>

So apparently I'm still missing something...

Arias answered 9/1, 2012 at 15:30 Comment(2)
@jcolebrand- You need to add an XmlElement attribute to the ListType property of the DeviceList class. See my updated below.Ommatidium
@jcolebrand- Here's some good reference on XML serializaton: msdn.microsoft.com/en-us/library/2baksw0z(v=vs.80).aspxOmmatidium
A
16

Using part of Dave's answer above, I found that it was best to use the property in the declaring class like this: (note the lack of attributes)

public DeviceList device_list { get; set; }

and then update the DeviceList class like this:

[XmlType("device_list")]
[Serializable]
public class DeviceList {
    [XmlAttribute]
    public string type { get; set; }

    [XmlElement( "item" )]
    public ListItem[] items { get; set; }
}

and keep the original ListItem class

public class ListItem {
    [XmlAttribute]
    public string type { get; set; }

    [XmlText]
    public string Value { get; set; }
}

and my serialization is as expected:

<device_list type="list">
  <item type="MAC">1234567</item>
  <item type="MAC">123456890</item>
</device_list>
Arias answered 9/1, 2012 at 16:16 Comment(2)
It took me a while to figure out that adding the [XmlElement] attribute is the key here. According to MSDN: If you apply the XmlElementAttribute to a field or property that returns an array, the items in the array are encoded as a sequence of XML elements. In contrast if an XmlElementAttribute is not applied to such a field or property, the items in the array are encoded as a sequence of elements, nested under an element named after the field or property.Canara
This works great. It seems that you can even apply multiple XmlElement references if you have more than one type of child with the same parent type.Tamberg
O
4

Instead of using a ListItem[], derive a new class from List<T> called DeviceList:

public class DeviceList : List<ListItem>
{
   [XmlElement(ElementName = "type")]
   public string ListType {get;set;}

}

Then, in a containing class use that class to serialize the XML. The type value can be included as an element of the parent node, depending on the way you configure the serialization. I don't remember the exact syntax, but I think class properties are added as node elements by default.

Containing class:

public class SerializeMyStuff
{
   public SeriazlieMyStuff()
   {
       ListOfDevices = new DeviceList();
       ListOfDevices.ListType = "list";
   }

   [XmlArray( "device_list" ), XmlArrayItem("item")]
   public DeviceList ListOfDevices {get;set;}
}
Ommatidium answered 9/1, 2012 at 15:37 Comment(4)
so where I use [XmlArray( "device_list" ), XmlArrayItem("item")] public ListItem[] device_list { get; set; } you're suggesting I should do what instead? Sorry for being dense, I just feel like I'm missing something here ...Arias
ListItem[] is a primitive array of ListItems. If you create a new class that derives from List<T>, you can add a new "type" property to that class. That will give you the ability to set the "type" element in the serialized XML.Ommatidium
Updated the Q, so you can see what I'm doing and where I'm not seeing the serialization. What am I doing wrong?Arias
Actually, no, that didn't work either .. but I found a solutionArias
P
3

You could also achieve the desired behaviour by implementing [IXmlSerializable][1] in your container class:

With the below code I get the following markup:

<?xml version="1.0"?>
<DeviceList type="list">
  <Item xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" type="MAC">11:22:33:44:55:66:77:88</Item>
  <Item xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" type="MAC">11:22:33:44:55:66:77:89</Item>
  <Item xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" type="MAC">11:22:33:44:55:66:77:8A</Item>
</DeviceList>

code:

public class Item
{
    [XmlAttribute("type")]
    public string Type { get; set; }

    [XmlText]
    public string Value { get; set; }
}

public class DeviceList : IXmlSerializable
{
    public string Type { get; set; }

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


    public System.Xml.Schema.XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(System.Xml.XmlReader reader)
    {
        reader.MoveToContent();
    }

    public void WriteXml(System.Xml.XmlWriter writer)
    {
        writer.WriteAttributeString("type", Type);


        XmlSerializer serializer = new XmlSerializer(typeof(Item));
        foreach (var item in Items)
        {
            serializer.Serialize(writer, item);
        }
    }
}

I use the following code in my main method:

var dlist = new DeviceList
                {
                    Type = "list",
                    Items = new List<Item>
                                {
                                    new Item {Type = "MAC", Value = "11:22:33:44:55:66:77:88"},
                                    new Item {Type = "MAC", Value = "11:22:33:44:55:66:77:89"},
                                    new Item {Type = "MAC", Value = "11:22:33:44:55:66:77:8A"},
                                }
                };


using(FileStream stream = new FileStream(@"D:\jcoletest.xml", FileMode.Create, FileAccess.Write))
{
    new XmlSerializer(typeof (DeviceList)).Serialize(stream, dlist);
}

For more information look at this tutorial here

Pamphylia answered 9/1, 2012 at 16:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.