1

I have the following xml format:

<FavouriteSettings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Customer>
    <ID>12</ID>
    <ID>2</ID>
    <ID>5</ID>
  </Customer>

  <Supplier>
    <ID>158</ID>
    <ID>23</ID>
    <ID>598</ID>
  </Supplier>
</FavouriteSettings>

=============================================================
I will have a class like below

class FavouriteList
{
   public string Name; // this name will be "Customer" and "Supplier"
   public List<int> aList; // to store those "ID" value
}

Class FavouriteSettings
{
   public List<FavouriteList> bigList;
}

What do I have to do or change to make this class work with XMLSerializer to generate the XML file like that format and deserialize back to the list and class as FavouriteList?

1
  • (added second example showing LINQ-to-XML with your existing types) Commented Oct 1, 2009 at 6:01

5 Answers 5

3

XmlSerializer is designed to be a very direct translation of your objects to xml; you can use IXmlSerializable, but it is rarely worth it. You would do better to create objects that mirror the xml structure. Or, simpler - use xsd to do it for you:

xsd example.xml
xsd example.xsd /classes

Or I suspect the following would work (untested):

using System.Collections.Generic;
using System.Xml.Serialization;
public class FavoriteSettings
{
    [XmlArray("Customer")]
    [XmlArrayItem("ID")]
    public List<int> Customers { get; set; }

    [XmlArray("Supplier")]
    [XmlArrayItem("ID")]
    public List<int> Suppliers { get; set; }
}

In particular; if you want the element names ("Customer" etc) to vary based on data ("Name" etc) - then it isn't going to hapopen unless you use IXmlSerializable, or write it yourself with XDocument (or similar). For simple data like this, maybe XDocument is a viable option? But then you make a lot of extra work, especially during deserialization.


Here's an example using your existing class via LINQ-to-XML:

static class Program
{
    static void Main() {
        var favs = new FavouriteSettings
        {
            bigList = new List<FavouriteList>
            {
                new FavouriteList {
                    Name = "Customer",
                    aList = new List<int>{
                        12,2,5
                    }
                }, new FavouriteList {
                    Name = "Supplier",
                    aList = new List<int>{
                        158, 23, 598
                    }
                }
            }
        };
        var el = new XElement("FavoriteSettings",
            from fav in favs.bigList
            select new XElement(fav.Name,
                from item in fav.aList
                select new XElement("ID", item)));

        string xml = el.ToString();
        Console.WriteLine(xml);

        el = XElement.Parse(xml);
        favs = new FavouriteSettings
        {
            bigList = new List<FavouriteList>(
                from outer in el.Elements()
                select new FavouriteList
                {
                    Name = outer.Name.LocalName,
                    aList = new List<int>(
                        from id in outer.Elements("ID")
                        select (int)id
                    )
                })
        };
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

It's actually possible to hack XmlSerializer to do this if you're willing to add some public members that are there solely for XmlSerializer to use - see my answer. It's still ugly, though.
2

You can use the following classes along with the XmlSerializer to serialize/deserialize to/from your desired XML:

  public class FavouriteSettings
  {
    public ID[] Customer
    {
      get;
      set;
    }

    public ID[] Supplier
    {
      get;
      set;
    }
  }

  public class ID
  {
    [XmlText()]
    public int Value
    {
      get;
      set;
    }
  }

Comments

1

If you must keep all existing names in the class definition as is, and introducing a class hierarchy for Customer and Supplier is out of question, then you'll need a hack like this:

public class FavouriteList
{
   [XmlIgnore]
   public string Name; // this name will be "Customer" and "Supplier"

   [XmlElement("ID")]
   public List<int> aList; // to store those "ID" value
}

public class FavouriteSettings
{
   [XmlIgnore]
   public List<FavouriteList> bigList;

   [XmlElement("Customer")]
   public FavouriteList[] Customers
   {
       get { return bigList.Where(fl => fl.Name == "Customer").ToArray(); }
       set { bigList.AddRange(value); }
   }

   [XmlElement("Supplier")]
   public FavouriteList[] Suppliers
   {
       get { return bigList.Where(fl => fl.Name == "Supplier").ToArray(); }
       set { bigList.AddRange(value); }
   }
}

3 Comments

Re your comment; I guess it depends on whether "Customer"/"Supplier" are fixed or dynamic - i.e. based on data.
You mean, whether the list of "types" is fixed at compile-time, or extensible at run-time?
I think I learn new things everyday... as I am just a new Linq. Thanks PM
0

XMLSerializer (the regular one) like to do its own thing, and the result is often rather ugly. You can semi-control the serialization by implementing IXmlSerializable, however if you have anything like "List" in there, .NET will butcher it.

I think you could get exactly that result using the DataContractXmlSerializer instead. Tag the class with [DataContract] and put a [DataMember] over each element you want serialized.

1 Comment

DataContractXmlSerializer? Is there such a beast? DataContractSerializer, otoh is a poor choice for specific xml; very little control over things like attributes, for example. For xml, use XmlSerializer, or write it yourself with XDocument etc.
0

I believe you can use attributes to control the way the data is serialized. See this MSDN article: "Attributes That Control XML Serialization".

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.