简体   繁体   中英

Parse XML with Linq with multiple child elements

This is my first question on SO, please let me know if I am doing anything wrong!

I am trying to parse an XML similar to this:

<LiveUpdate>
  <CityID>F0A21EA2</CityID>
  <CityName>CityTown</CityName>
  <UserName>john</UserName>
  <ApplicationDetails>
    <ApplicationDetail
      Application="AC"
      Licensed="true"
      Version="2015.2"
      Patch="0001"
    />
    <ApplicationDetail
      Application="AP"
      Licensed="true"
      Version="2015.2"
      Patch="0002"
    />
  </ApplicationDetails>
</LiveUpdate>

I have classes that look like this:

public class Client
{
    public string cityID { get; set; }
    public string cityName { get; set; }
    public string userName { get; set; }
    public List<Apps> appList { get; set; }
    }
public class Apps
{
    public string app { get; set; }
    public string licensed { get; set; }
    public string version { get; set; }
    public string patch { get; set; }
}

I need to be able to have a client class with a list of all the application details to be iterated over.

So far the best I've come up with is:

XDocument xml = XDocument.Load(@"C:\blah\Desktop\1.xml");
var liveUpdate = xml.Root;
var clients = (from e in liveUpdate.Elements()
               select new Client()
               {
                   cityID = e.Element("CityID").Value,
                   cityName = e.Element("CityName").Value,
                   userName = e.Element("UserName").Value,
                   appList = e.Elements("ApplicationDetails")
                              .Select(a => new Apps()
                              {
                                  app = a.Element("Application").Value,
                                  licensed = a.Element("Licensed").Value,
                                  version = a.Element("Version").Value,
                                  patch = a.Element("Patch").Value
                              }).ToList()
               });

However, I'm currently running into an error that says Object reference not set to an instance of an object. I've seen some similar examples on here, but not that deal with data before the multiple children.

I'm fairly new to XML and Linq so any help here would be greatly appreciated!

Since you have already defined a class into which you wish to deserialize, you can use XmlSerializer to deserialize it for you.

First, let's rename some of your property names to more closely match the XML and c# naming conventions :

[XmlRoot("LiveUpdate")]
public class Client
{
    public string CityID { get; set; }
    public string CityName { get; set; }
    public string UserName { get; set; }

    [XmlArray("ApplicationDetails")]
    [XmlArrayItem("ApplicationDetail")]
    public List<Apps> AppList { get; set; }
}

public class Apps
{
    [XmlAttribute]
    public string Application { get; set; }
    [XmlAttribute]
    public bool Licensed { get; set; }
    [XmlAttribute]
    public string Version { get; set; }
    [XmlAttribute]
    public string Patch { get; set; }
}

Then add the following extension methods:

public static class XmlSerializationHelper
{
    public static T LoadFromXML<T>(this string xmlString)
    {
        using (StringReader reader = new StringReader(xmlString))
        {
            object result = new XmlSerializer(typeof(T)).Deserialize(reader);
            if (result is T)
            {
                return (T)result;
            }
        }
        return default(T);
    }

    public static T LoadFromFile<T>(string filename)
    {
        using (var fs = new FileStream(filename, FileMode.Open))
        {
            object result =  new XmlSerializer(typeof(T)).Deserialize(fs);
            if (result is T)
            {
                return (T)result;
            }
        }
        return default(T);
    }
}

Now you can deserialize from your XML file as follows:

        string fileName = @"C:\blah\Desktop\1.xml";
        var client = XmlSerializationHelper.LoadFromFile<Client>(fileName);

I manually updated your Client class to map correctly to the provided XML, but if you wanted to do it automatically, see here: Generate C# class from XML .

  1. Your XML only contains one LiveUpdate tag, so rather than iterating over all of the elements inside of it, you just want to look at the Root element.
  2. In ApplicationDetails, Application , Licensed and such are attributes , not elements . Use .Attribute() to access them.
  3. ApplicationDetails is a single tag, and inside it you have ApplicationDetail tags.
  4. There is no DateTime element in your LiveUpdate tag.

This works:

    var liveUpdate = xml.Root;
    var e = liveUpdate;
    var clients = new Client()
                {
                    cityID = e.Element("CityID").Value,
                    cityName = e.Element("CityName").Value,
                    userName = e.Element("UserName").Value,
                    //dateTime = e.Element("DateTime").Value,
                    appList = e.Element("ApplicationDetails").Elements("ApplicationDetail")
                                .Select(a => new Apps()
                                {
                                    app = a.Attribute("Application").Value,
                                    licensed = a.Attribute("Licensed").Value,
                                    version = a.Attribute("Version").Value,
                                    patch = a.Attribute("Patch").Value
                                }).ToList()
                };

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