简体   繁体   中英

How to parse xml with LINQ to an Object with XDocument

I have an xml file as below:

<Message xsi:schemaLocation ="..">
    <Header>..</Header>
    <Body>
        <History 
            xmlns="..">
            <Number></Number>
            <Name></Name>
            <Item>
                <CreateDate>..</CreateDate>
                <Type>..</Type>
                <Description></Description>
            </Item>
            <Item>
                <CreateDate>..</CreateDate>
                <Type>..</Type>
                <Description>1..</Description>
                <Description>2..</Description>
            </Item>
        </History>
    </Body>
</Message>

I would like to create and object from this as History object.

public class History
{
    public string Name { get; set; }
    public string Number { get; set; }
    public List<Item> Items { get; set; }

}



    var xElement = XDocument.Parse(xmlString);

    XElement body = (XElement)xElement.Root.LastNode;
    XElement historyElement = (XElement)body.LastNode;

    var history = new History
    {
        Name = (string)historyElement.Element("Name"),
        Number = (string)historyElement.Element("Number"),
        Items = (
               from e in historyElement.Elements("Item")
               select new Item
               {
                   CraeteDate = DateTime.Parse(e.Element("CreateDate").Value),
                   Type = (string)e.Element("Type").Value,
                   Description = string.Join(",",
                       from p in e.Elements("Description") select (string)p.Element("Description"))
               }).ToList()

    };

Why this does not work?

The values are always null.

It seems that "historyElement.Element("Name")" is always null even there is an element and value for the element.

Any idea what am I missing?

Thanks

It's due to the namespace, try doing this:

XNamespace ns =  "http://schemas.microsoft.com/search/local/ws/rest/v1";// the namespace you have in the history element
var xElement = XDocument.Parse(xmlString);

var history= xElement.Descendants(ns+"History")
                     .Select(historyElement=>new History{ Name = (string)historyElement.Element(ns+"Name"),
                                                          Number = (string)historyElement.Element(ns+"Number"),
                                                          Items = (from e in historyElement.Elements(ns+"Item")
                                                                   select new Item
                                                                          {
                                                                            CraeteDate= DateTime.Parse(e.Element(ns+"CreateDate").Value),
                                                                            Type = (string) e.Element(ns+"Type").Value,
                                                                            Description= string.Join(",",
                                                                             from p in e.Elements(ns+"Description") select (string)p)
                                                                           }).ToList()
                                                          }).FirstOrDefault();

If you want to read more about this subject, take a look this link

A couple of minor things here, the xml was malformed here. So had to take a while to test and make it work.

You have an xsi in the front which I assume should be somewhere mentioned in the xsd.

Turns out you have to append the namespace if your xml node has a namespace attached to it as you are parsing the xml tree here:

My sample solution looked like this:

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

namespace ConsoleApplication1
{
    public class History
    {
        public string Name { get; set; }
        public string Number { get; set; }
        public List<Item> Items { get; set; }

    }

    public class Item
    {
        public DateTime? CreateDate { get; set; }
        public string Type { get; set; }
        public string Description { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            string xmlString =
                @"<Message>
                    <Header>..</Header>
                    <Body>
                        <History 
                            xmlns=""http://schemas.somewhere.com/types/history"">
                            <Number>12</Number>
                            <Name>History Name</Name>
                            <Item>
                                <CreateDate></CreateDate>
                                <Type>Item 1 Type</Type>
                                <Description>Item 1 Description</Description>
                            </Item>
                            <Item>
                                <CreateDate></CreateDate>
                                <Type>Item 2 Type</Type>
                                <Description>Item 2 Description 1</Description>
                                <Description>Item 2 Description 2</Description>
                            </Item>
                        </History>
                    </Body>
                </Message>";


            XNamespace ns = "http://schemas.somewhere.com/types/history";
            var xElement = XDocument.Parse(xmlString);

            var historyObject = xElement.Descendants(ns +"History")
                .Select(historyElement => new History
                {
                    Name = historyElement.Element(ns + "Name")?.Value,
                    Number = historyElement.Element(ns + "Number")?.Value,
                    Items = historyElement.Elements(ns + "Item").Select(x => new Item()
                    {
                        CreateDate = DateTime.Parse(x.Element(ns + "CreateDate")?.Value),
                        Type = x.Element(ns + "Type")?.Value,
                        Description = string.Join(",", x.Elements(ns + "Description").Select(elem=>elem.Value))
                    }).ToList()
                }).FirstOrDefault();
        }
    }
}

If you dont wan't to care about finding the namespace, you might want to try the following:

    var document = XDocument.Parse(xmlString);
    var historyObject2 = document.Root.Descendants()
        .Where(x=>x.Name.LocalName == "History")
        .Select(historyElement => new History
        {
            Name = historyElement.Element(historyElement.Name.Namespace + "Name")?.Value,
            Number = historyElement.Element(historyElement.Name.Namespace+ "Number")?.Value,
            Items = historyElement.Elements(historyElement.Name.Namespace + "Item").Select(x => new Item()
            {
                //CreateDate = DateTime.Parse(x.Element("CreateDate")?.Value),
                Type = x.Element(historyElement.Name.Namespace + "Type")?.Value,
                Description = string.Join(",", x.Elements(historyElement.Name.Namespace + "Description").Select(elem => elem.Value))
            }).ToList()
        }).FirstOrDefault();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM