简体   繁体   中英

Parse XML at different levels

I have the below XML where under forecast there are many "time" nodes ... over 20, I just left 3 as an example below. I am able to return data for Location and also the attributes of Time, but I'm having difficulty with precipitation and clouds.

<weatherdata>
    <location>
      <name>Tokyo</name>
      <country>JP</country>
    </location>
    <forecast>
      <time from="2017-08-19T12:00:00" to="2017-08-19T15:00:00">   
          <precipitation unit="3h" value="0.1925" type="rain"/>   
          <clouds value="overcast clouds" all="88" unit="%"/>
      </time>
      <time from="2017-08-19T15:00:00" to="2017-08-19T18:00:00">
        <precipitation unit="3h" value="0.085000000000001" type="rain"/>
        <clouds value="overcast clouds" all="92" unit="%"/>
      </time>
      <time from="2017-08-19T18:00:00" to="2017-08-19T21:00:00">
        <precipitation unit="3h" value="0.7125" type="rain"/>
        <clouds value="overcast clouds" all="88" unit="%"/>
      </time>
    </forecast>
</weatherdata>

I marked in the code below as a C# commentwhere my problem (the one I know of!) is.

using (respStream = resp.GetResponseStream())
{
    location = new WeatherData.Location.LocationData();

    XmlDocument xml = Utility.retrieveXMLDocFromResponse(respStream, "/weatherdata");
    location.country = xml.GetElementsByTagName("country")[0].InnerText;
    location.name = xml.GetElementsByTagName("name")[0].InnerText;

    forecastList = new List<WeatherData.Forecast.Time>();                       
    XmlNodeList xnlNodes = xml.DocumentElement.SelectNodes("/weatherdata/forecast");
    foreach (XmlNode xndNode in xnlNodes)
    {
        WeatherData.Forecast.Time time = new WeatherData.Forecast.Time();
        time.from = xndNode["time"].GetAttribute("from");
        time.to = xndNode["time"].GetAttribute("to");                  

        // Here I am able to get data for clouds and preciptation, but the loop goes through all the 20 nodes.  I just want to return the "preciptation" and "clouds" of the relevant "time" node.
        XmlNodeList xnlTNodes = xml.DocumentElement.SelectNodes("/weatherdata/forecast/time");
        foreach (XmlNode xmlTimeNode in xnlTNodes)
        {
            time.clouds = xmlTimeNode["clouds"].GetAttribute("value");
            time.precipitation = xmlTimeNode["precipitation"].GetAttribute("type");
        }
        forecastList.Add(time);
    }
}

The WeatherData class simply contains getter/setter methods to store data. My other two references methods are below doing the same thing but one returns an XmlNodeList and the other an XML document.
They are static just so that I don't create reference to the class for now:

 public static XmlNodeList retrieveXMLResponse(Stream stream, String baseNode)
{
    StreamReader reader = null;
    XmlElement xelRoot = null;
    XmlNodeList xnlNodes = null;

    try
    {
        reader = new StreamReader(stream, Encoding.UTF8);
        string responseString = reader.ReadToEnd();
        XmlDocument xmlDoc = new XmlDocument();
        xmlDoc.LoadXml(responseString);

        xelRoot = xmlDoc.DocumentElement;
        xnlNodes = xelRoot.SelectNodes(baseNode);
    }
    finally
    {
        reader.Close();
    }

    return xnlNodes;
}

public static XmlDocument retrieveXMLDocFromResponse(Stream stream, String baseNode)
{
    StreamReader reader = null;
    XmlDocument xmlDoc = null;

    try
    {
        reader = new StreamReader(stream, Encoding.UTF8);
        string responseString = reader.ReadToEnd();
        xmlDoc = new XmlDocument();
        xmlDoc.LoadXml(responseString);
    }
    finally
    {
        reader.Close();   
    }

    return xmlDoc;
}

As proposed in my comment, simply do not select the forecast node, iterate over the time nodes instead.

using (respStream = resp.GetResponseStream())
{
    location = new WeatherData.Location.LocationData();

    XmlDocument xml = Utility.retrieveXMLDocFromResponse(respStream, "/weatherdata");
    location.country = xml.GetElementsByTagName("country")[0].InnerText;
    location.name = xml.GetElementsByTagName("name")[0].InnerText;

    forecastList = new List<WeatherData.Forecast.Time>();                       
    XmlNodeList xnlTNodes = xml.DocumentElement.SelectNodes("/weatherdata/forecast/time");
    foreach (XmlNode xmlTimeNode in xnlTNodes)
    {

        WeatherData.Forecast.Time time = new WeatherData.Forecast.Time();
        time.from = xmlTimeNode .GetAttribute("from");
        time.to = xmlTimeNode .GetAttribute("to");                           
        time.clouds = xmlTimeNode["clouds"].GetAttribute("value");
        time.precipitation = xmlTimeNode["precipitation"].GetAttribute("type");
         forecastList.Add(time);
     }

}

I like using xml linq :

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

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

            var weather = doc.Elements("weatherdata").Select(x => new {
                name = (string)x.Descendants("name").FirstOrDefault(),
                country = (string)x.Descendants("country").FirstOrDefault(),
                forecasts = x.Descendants("time").Select( y => new {
                    from = (DateTime)y.Attribute("from"),
                    to = (DateTime)y.Attribute("to"),
                    precipitation = y.Descendants("precipitation").Select(z => new {
                        unit = (string)z.Attribute("unit"),
                        value = (float)z.Attribute("value"),
                        type = (string)z.Attribute("type")
                    }).FirstOrDefault(),
                    clouds = y.Descendants("clouds").Select(z => new {
                        value = (string)z.Attribute("value"),
                        all = (int)z.Attribute("all"),
                        unit = (string)z.Attribute("unit")
                    }).FirstOrDefault()
                }).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