XmlSerializer List Item Element Name

This is mine test code

using System.Collections.Generic;
using System.Xml.Serialization;

namespace TestLoadingMultiXml
{
[XmlRoot(ElementName=@"main")]
public class XmlMain
{
    private XmlDataTest data;

    [XmlElement(ElementName=@"datalist")]
    public XmlDataTest Data
    {
        get { return data; }
        set { data = value; }
    } // public XmlDataTest Data

    public XmlMain()
    {
        data = new XmlDataTest();
    }
}

[XmlRoot(ElementName=@"xmldata")]
public class XmlDataTest
{
    private List<DataDetails> listData;

    [XmlElement(ElementName=@"listdata")]
    public List<DataDetails> Data
    {
        get { return listData; }
        set { listData = value; }
    }

    public XmlDataTest()
    {
        listData = new List<DataDetails>();
        for (int i = 0; i < 10; i++)
        {
            DataDetails d = new DataDetails(string.Format("{0}", i));
            listData.Add(d);
        } // for (int i=0; i < 10; i++)
    } // public XmlDataTest()
} // class XmlDataTest

[XmlRoot(ElementName=@"datadetail")]
public class DataDetails
{
    private string name;

    [XmlAttribute(AttributeName=@"name")]
    public string Name
    {
        get
        {
            return name;
        }
        set { name = value; }
    }

    public DataDetails(string _value)
    {
        this.name = _value;
    } // public DataDetails(string _value)

    public DataDetails()
    {
        this.name = "";
    } // public DataDetails()
} // public class DataDetails
}

and running program

using System;
using System.IO;
using System.Windows.Forms;
using System.Xml.Serialization;

namespace TestLoadingMultiXml
{
    public partial class Form1 : Form
    {
    private XmlMain xt;
    private string xname = @"x.xml";

    public Form1()
    {
        InitializeComponent();
        this.FormClosing += new FormClosingEventHandler(Form1_FormClosing);
    }

    void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        XmlSerializer x = new XmlSerializer(typeof(XmlMain));
        FileStream fs = new FileStream(xname, FileMode.Create);
        x.Serialize(fs, xt);
        fs.Close();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        xt = new XmlMain();
        xname = Directory.GetCurrentDirectory() + @"\" + xname;
        if (File.Exists(xname))
        {
            XmlSerializer x = new XmlSerializer(typeof(XmlMain));
            FileStream fs = new FileStream(xname, FileMode.Open);
            xt = (XmlMain)x.Deserialize(fs);
            fs.Close();
        } // if (File.Exists(xname))
    }
}
}

Also this is the result

<?xml version="1.0"?>
<main xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"     xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <datalist>
    <listdata name="0" />
    <listdata name="1" />
    <listdata name="2" />
    <listdata name="3" />
    <listdata name="4" />
    <listdata name="5" />
    <listdata name="6" />
    <listdata name="7" />
    <listdata name="8" />
    <listdata name="9" />
  </datalist>
</main>

I had the identical problem with my serializer. None of the answers above worked exactly. I found that the XmlRoot attribute on the Human class is plainly ignored because it isn't the root element of the document. Wrapping the list in a context object wasn't an option for me because I can't change the XML schema. The solution is to change up the Persons class. Instead of subclassing a generic list, you wrap it in an object and change how it is serialized. See the sample code below:

[XmlRoot("Persons")]
public class Persons 
{
    public Persons ()
    {
        People = new List<Human>();
    }

    [XmlElement("Person")]
    public List<Human> People 
    { get; set; }
}

public class Human
{
    public Human()
    {
    }

    public Human(string name)
    {
        Name = name;
    }

    [XmlElement("Name")]
    public string Name { get; set; }
}

Serializing your generic list using XmlElement means that it won't put the wrapper element around your list like XmlArray does or like the subclassing does. It also gives you the bonus option of adding attributes to the Persons class, which is where I got the idea from:

How do I add a attribute to a XmlArray element (XML Serialization)?


I don't think there is a way for you to control the name of the generated array elements.

If you can however wrap the Persons collection inside another class you will then have complete control over the generated output using XmlArrayAttribute and XmlArrayItemAttribute.

If you cannot create this new class you can resort to implementing IXmlSerializable, but this is much more complex.

An example for the first alternative follows:

[XmlRoot("Context")]
public class Context
{
    public Context() { this.Persons = new Persons(); }

    [XmlArray("Persons")]
    [XmlArrayItem("Person")]
    public Persons Persons { get; set; }
}

public class Persons : List<Human> { }

public class Human
{
    public Human() { }
    public Human(string name) { Name = name; }
    public string Name { get; set; }
}

class Program
{
    public static void Main(string[] args)
    {
        Context ctx = new Context();
        ctx.Persons.Add(new Human("john"));
        ctx.Persons.Add(new Human("jane"));

        var writer = new StringWriter();
        new XmlSerializer(typeof(Context)).Serialize(writer, ctx);

        Console.WriteLine(writer.ToString());
    }
}

Mark your class with the following attributes:

[XmlType("Account")]
[XmlRoot("Account")]

The XmlType attribute will result in the output requested in the OP. Per the documentation:

Controls the XML schema that is generated when the attribute target is serialized by the XmlSerializer