1

​Hi, I would like to deserialize (not so common) XML to object. Normal XML should looks like:

<library>
<books>
 <book name="1"><author></author><details></details></book>
 <book name="2"><author></author><details></details></book>
 <book name="2"><author></author><details></details></book>
</books>
</library>

As you can see there is 'books' branch inside which I have some 'book' elements. It is ok, easy to deserialize etc. However my XML looks different. Inside 'books' there are elements with random names. Instead of 'book' element there is element with book name itself (as element name). What is more inside these elements there are always the same elements like 'author' and 'details'.

Please have a look at this:

<library>
<books>
 <1><author></author><details></details></1>
 <2><author></author><details></details></2>
 <3><author></author><details></details></3>
</books>
</library>

Any suggestion how to create objects from the second XML?

cube
  • 65
  • 5
  • Take a look at this question: https://stackoverflow.com/questions/364253/how-to-deserialize-xml-document – Sebastian Hofmann Dec 07 '17 at 10:54
  • 1
    That's not even valid xml; xml element names can't start with numbers. Is that the *actual* xml? – Marc Gravell Dec 07 '17 at 10:56
  • @MarcGravell, yes, you are right. These names 1, 2 and 3 are not valid. Should be A, B and C or something. Other things are as described. – cube Dec 07 '17 at 11:06

2 Answers2

1

Using xml linq. I change xml so tagsw 1,2,3 are a,b,c since tags can't start with a number

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


namespace ConsoleApplication16
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {
            XDocument doc = XDocument.Load(FILENAME);

            Book.books = doc.Descendants("books").FirstOrDefault().Elements().Select(x => new Book() {
                name = x.Name.LocalName,
                author = (string)x.Element("author"),
                detail = (string)x.Element("details")
            }).ToList();
        }
    }

    public class Book
    {
        public static List<Book> books = new List<Book>();

        public string name { get; set;}
        public string author { get; set;}
        public string detail { get; set;}
    }



}
jdweng
  • 33,250
  • 2
  • 15
  • 20
0

XmlSerializer will hate that - it is used to situations where the elements are fixed. Options:

  • rewrite the xml completely and use XmlSerializer
  • use manual deserialization (like @jdweng's answer)
  • rewrite bits of the xml in-memory, and use XmlSerializer

For an example of the third option:

using System.Collections.Generic;
using System.Xml;
using System.Xml.Serialization;

static class P
{
    static void Main()
    {
        var xml = @"<library>
<books>
 <a><author>x</author><details>1</details></a>
 <b><author>y</author><details>2</details></b>
 <c><author>z</author><details>3</details></c>
</books>
</library>";
        var doc = new XmlDocument();
        doc.LoadXml(xml);

        var ser = new XmlSerializer(typeof(Book));

        List<Book> books = new List<Book>();
        foreach (XmlElement book in doc.SelectNodes("/library/books/*"))
        {
            var el = doc.CreateElement("book");
            el.SetAttribute("name", book.Name);
            foreach (XmlNode child in book.ChildNodes)
            {
                el.AppendChild(child.Clone());
            }

            using (var reader = new XmlNodeReader(el))
            {
                books.Add((Book)ser.Deserialize(reader));
            }
        }

        foreach(var book in books)
        {
            System.Console.WriteLine(book);
        }

    }
}
[XmlRoot("book")]
public class Book
{
    [XmlAttribute("name")]
    public string Name { get; set; }
    [XmlElement("author")]
    public string Author { get; set; }
    [XmlElement("details")]
    public string Details { get; set; }

    public override string ToString() => $"{Name} by {Author}: {Details}";
}

which outputs:

a by x: 1
b by y: 2
c by z: 3
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Cannot rewrite XML. I am not author of the file. I will choose the second or third solution (dependent on what will more fit requirements). Thank you both of you for help. – cube Dec 07 '17 at 11:27
  • @cube on the topic of "rewrite" - you can transform the xml to a different layout without changing the original. xslt is quite useful for that, but there's lots of ways of doing it. Or just work with what you have received. – Marc Gravell Dec 07 '17 at 11:59