19

I want to create a custom XML structure as follows:

<Hotels>
    <Hotel />
</Hotels>

I've created an implementation of List just to be able to do this. My code is as follows:

[XmlRootAttribute(ElementName="Hotels")]
public class HotelList: List<HotelBasic>

Because the class that List holds is not named Hotel but HotelBasic my xml is like

<Hotels>
   <HotelBasic />
</Hotels>

How do I fix this without having to implement ISerializable or IEnumerable?

abatishchev
  • 98,240
  • 88
  • 296
  • 433
Odys
  • 8,951
  • 10
  • 69
  • 111

3 Answers3

35
[XmlArray("Hotels")]
[XmlArrayItem(typeof(Hotel), ElementName="Hotel")]
public HotelList[] Messages { get; set; }

should produce:

<Hotels>
    <Hotel />
    <Hotel />
</Hotels>

[XmlRoot("Hotels")]
public class HotelList : IXmlSerializable
{
    public System.Xml.Schema.XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(XmlReader reader)
    {
        this.Hotels = XDocument.Load(reader)
                               .Select(h => new Hotel { Name = (string)h.Attribute("name") }
                               .ToList();
    }

    public void WriteXml(XmlWriter writer)
    {
        throw new NotSupportedException();
    }
}
abatishchev
  • 98,240
  • 88
  • 296
  • 433
  • I need to use List, not HotelBasic[] – Odys Nov 24 '11 at 08:52
  • @odyodyodys: 1. XmlSerializer supports only array. 2. You asked for `IEnumerable`, array implements it. 3. You do not want `List` because it's a expendable container, but list in xml is *final*. 4. You can create list calling `ToList()`. – abatishchev Nov 24 '11 at 08:54
  • I am using `List`s methods like Find and lots more. It would be just pain to reimplement `List` – Odys Nov 24 '11 at 08:57
  • @odyodyodys: You can call `ToList().Find(...)`, or use not XmlSerializer and set of attributes, but mark only root element and implement `IXmlSerializable` for custom structure. – abatishchev Nov 24 '11 at 09:00
  • @odyodyodys: I've updated my answer how to implement `IXmlSerializable`. – abatishchev Nov 24 '11 at 09:04
  • @Marc: I prefer LINQ to XML option as giving maximum flexibility in complex scenarios. Also saves from headache from infinite attribute decoration. – abatishchev Nov 24 '11 at 09:32
19

Assuming you are using XmlSerializer, if all you want to do is change how your HotelBasic class is serialized, you can use XmlTypeAttribute:

[XmlType(TypeName = "Hotel")]
public class HotelBasic
{
    public string Name { get; set; }
}

When used with your HotelList class it will be serialized as:

<Hotels xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Hotel>
    <Name>One</Name>
  </Hotel>
  <Hotel>
    <Name>Two</Name>
  </Hotel>
</Hotels>
madd0
  • 9,053
  • 3
  • 35
  • 62
8

I think madd0 shows the simplest option for you here, but just for completeness... personally I don't recommend the "serialize the list as the root object" - for various reasons (including: I've seen those attributes not work on at least on platform - might have been CF or SL, can't remember). Instead, I always advise using a custom root type:

[XmlRoot("Hotels")]
public class HotelResult // or something similar
{
    [XmlElement("Hotel")]
    public List<HotelBasic> Hotels { get { return hotel; } }

    private readonly List<HotelBasic> hotels = new List<HotelBasic>();
}

This will have the same xml structure, and allows greater flexibility (you can add other attributes / elements to the root), and doesn't bake List<T> into your type model (prefer encapsulation over inheritance, etc).

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900