views:

522

answers:

4

HI

Hope someone can help me on this.

Say, I have an object of class

[XmlRoot("Persons")]
PersonList : List<Human>

, when serialize this object to xml, by defualt it will produce something like this:

<Persons><Human>.......</Human><Human>.......</Human></Persons>

My question is what need to be done in order to change element Human to Person in the output? so the output would be :

<Persons><Person>.......</Person><Person>.......</Person></Persons>

and, how to deserialize the above xml to the PersonList class object?

Many Thanks,

Q

Per Nick's advice, Here are my testing codes:

[XmlRoot("Persons")]
public class Persons : List<Human>
{

}

[XmlRoot("Person")]
public class Human
{
    public Human()
    {
    }

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

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

}


void TestXmlSerialize()
    {
        Persons personList = new Persons();
        personList.Add(new Human("John"));
        personList.Add(new Human("Peter"));

        try
        {
            using (StringWriter writer = new StringWriter())
            {
                XmlSerializer serializer = new XmlSerializer(typeof(Persons));
                XmlWriterSettings settings = new XmlWriterSettings();
                settings.OmitXmlDeclaration = true;

                XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
                namespaces.Add(string.Empty, string.Empty);

                XmlWriter xmlWriter = XmlWriter.Create(writer, settings);
                serializer.Serialize(xmlWriter, personList, namespaces);

                Console.Out.WriteLine(writer.ToString());
            }
        }
        catch (Exception e)
        {
            Console.Out.WriteLine( e.ToString());
        }
    }

The output of the testing code is:

<Persons><Human><Name>John</Name></Human><Human><Name>Peter</Name></Human></Persons>

As the output shows, the [XmlRoot("Person")] attribute on Human class does not change the tag to Person from Human.

Any thoughts?

Thanks,

Q

+4  A: 

Set the XmlRoot on Human to:

[XmlRoot("Person")]

Sidebar:

Persons should probably be People

Cory Charlton
This works if serializing the Human class directly, and does not work as child item when serializing the Persons class.
Qstonr
+1  A: 

If you don't have access to the source for the Human class (in which case, setting XmlRoot is not possible), you can create an XmlElementAttribute, then add it to an XmlAttributeOverride and use that when creating an instance of your XmlSerializer. See this MSDN article for more details.

Nick
@Nick: +1: For providing an alternative when he doesn't have access to `Human`, hadn't considered that. Another, slightly less involved, solution for this case would be to use Human (assuming allowed) as a base in a new class and set the XmlRoot there. It's hackish but ... :-)
Cory Charlton
Thanks, but I can't seem to make it work. I wonder it is because I am trying to override the element name for the items in a generic list, instead of overriding an member of an object.
Qstonr
@Qstonr - I would suggest updating the question with the code you tried to make work. It's really hard to troubleshoot without code to review.
Nick
+3  A: 

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());
    }
}
João Angelo
Joao has it right, if you want to use the XML attributes you need to root your arrays.
Steven Pardo
+1  A: 

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:

http://stackoverflow.com/questions/1052556/how-do-i-add-a-attribute-to-a-xmlarray-element-xml-serialization

Steve