3

I have XML Serializable class with property Name

[Serializable]
public class Item
{
    [XmlElement("name")]
    public string Name { get; set; }
}

and I want it to be able to deserialize XML file that I have in two ways:

<item>
    <name>Name</name>
</item>

and

<item>
    <name value="Name" />
</item>

The first works fine but what should I do to be able to deserialize the second also with the same class?

Brazol
  • 435
  • 1
  • 6
  • 17

3 Answers3

3

I found another way to solve my problem using only one class maybe someone will find this useful

[Serializable]
public class Item
{
    [XmlElement("name")]
    public NameElement NameElement { get; set; }
}

public class NameElement
{
    [XmlAttribute("value")]
    public string Value { get; set; }

    [XmlText]
    public string Text { get; set; }

    [XmlIgnore]
    public string Name
    {
        get { return String.IsNullOrEmpty(this.Value) ? this.Text : this.Value; } 
        set { this.Value = value; }
    }
}

Maybe it's not super elegant but it works in both cases and uses the same class.

Brazol
  • 435
  • 1
  • 6
  • 17
2

XML Serialization attributes work both with serialization and deserialization. If we'll assume that it might be possible to use attributes for deserializing instance of Item from two different xml structures, then how serialization should work - should it serialize instance name to element value, or to attribute? Or to both? That's why you cannot deserialize two different xml structures into single class. Use two different classes or deserialize it manually without usage of XML Serialization attributes.

Sergey Berezovskiy
  • 232,247
  • 41
  • 429
  • 459
  • Right, I thought that maybe xml element's inner text is somehow translated into value attribute. I'm using external api and depending on the request I get items with different convention of name element :/ – Brazol Oct 15 '15 at 09:50
  • 1
    Then probably best solution for you is to create another `Item2` class for different convention (of course you should use better name which describes difference between your request types). And deserialize response to `Item2` if you send request of different type. You can make both `Item` and `Item2` implement same interface. So other parts of your code can use it without knowing which exact type you passed. – Sergey Berezovskiy Oct 15 '15 at 09:55
0

Since you have mentioned that XML data is coming from external sources, so obviously you don't have control over that.

Therefore you can follow any of the option as below:

  1. Create separate class per XML data structure, because as far I know there is no way to control XML Deserialization when using XmlSerializer
  2. You can use XDocument to read the XML by self, to overcome this limitation.

If going by second idea, I have created small Console Application to demonstrate that.

Main piece of code is as below:

MemoryStream xmlStream = new MemoryStream(Encoding.UTF8.GetBytes(xmlData));
XDocument doc = XDocument.Load(xmlStream);

var records = from record in doc.Descendants("item").Descendants()
              select new Item(!record.IsEmpty ? record.Value : record.Attribute("value").Value);

Here I'm reading the element using LinqToXml and checking if the element is not empty, i.e. Value is not blank, then use Value otherwise read the value from element's Attribute.

Console application (Complete code):

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Xml.Serialization;

namespace Console.TestApp
{
    class Program
    {
        static string xmltypeFirst = @"<item>
    <name>John</name>
</item>";

        static string xmltypeSecond = @"<item>
    <name value='Smith' />
</item>";

        static void Main(string[] args)
        {
            var data = xmltypeFirst;
            var result = Deserialize(data).ToList();
            Console.WriteLine("Name: " + result[0].Name);

            data = xmltypeSecond;
            result = Deserialize(data).ToList();
            Console.WriteLine("Name: " + result[0].Name);

            Console.WriteLine("Press any to key to exit..");
            Console.ReadLine();
        }

        private static IEnumerable<Item> Deserialize(string xmlData)
        {
            MemoryStream xmlStream = new MemoryStream(Encoding.UTF8.GetBytes(xmlData));
            XDocument doc = XDocument.Load(xmlStream);

            var records = from record in doc.Descendants("item").Descendants()
                          select new Item(!record.IsEmpty ? record.Value : record.Attribute("value").Value);
            return records;
        }
    }

    [Serializable]
    public class Item
    {
        public Item(string name)
        {
            this.Name = name;
        }

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

Note: To run this you will need to add reference to System.Xml.Linq.dll in your project.

Reference: here

Community
  • 1
  • 1
Harsh Baid
  • 7,199
  • 5
  • 48
  • 92
  • Nice idea but my example was only to illustrate the problem and the xml is way more complicated so it would be much more work to deserialize it using XmlDocument than using Serializable class. – Brazol Oct 15 '15 at 12:28