views:

180

answers:

5

I have the following xml format:

<FavouriteSettings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;
  <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;
}

QUESTION: what do i have to do or change to make this class above to use XMLSerializer to generate the XML file like that format and DeSerializer back to the list and class as FavouriteList ?

Thanks

A: 

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.

Robert Fraser
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.
Marc Gravell
thanks but I have to use XmlSerializer
A: 

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

StarSignLeo
thanks for pointing out...
+1  A: 

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;
    }
  }
Tuzo
great!! for pointing this out... I have not thought about it...
+3  A: 

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
                    )
                })
        };
    }
}
Marc Gravell
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.
Pavel Minaev
great example and full codes too... thanks MG
+1  A: 

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); }
   }
}
Pavel Minaev
Re your comment; I guess it depends on whether "Customer"/"Supplier" are fixed or dynamic - i.e. based on data.
Marc Gravell
You mean, whether the list of "types" is fixed at compile-time, or extensible at run-time?
Pavel Minaev
I think I learn new things everyday... as I am just a new Linq.Thanks PM