Search code examples
c#asp.netxmlparsingxmlreader

How to parse an XML element that has an attribute inside it? Asp.net C#


XML file:

<weatherdata>
<forecast>

<time day="2017-04-18">
    <temperature day="-4.26" min="-6.54" max="-4.26" night="-6.54" eve="-4.26" morn="-4.26"/>
</time>

<time day="2017-04-19">
    <temperature day="3.51" min="-5.41" max="4.49" night="-0.63" eve="4.27" morn="-5.41"/>
</time>

</forecast>
</weatherdata>

I need to parse the file and get the correct element depending on which day="xxxx-xx-xx" I want to use. Then when I have the right time, I would like to create new strings for temperature_day, temperature_min, etc. with their correct values.

I've tried dozens of XmlReader variants, and I want to use it because it seems like such a simple thing to do. Here's what I have so far. If I can name the "reader.Name ==" why can't I just ask for the attribute name as well?

XmlReader reader = XmlReader.Create(URL);
    while (reader.Read())
    {
        if (reader.NodeType == XmlNodeType.Element && reader.Name == "time" && reader.HasAttributes)
        {
            // DATE comes from a asp.net calendar, the format is correct
            if (DATE == reader.GetAttribute("day"))
            {
               if (reader.NodeType == XmlNodeType.Element && reader.Name == "temperature" && reader.HasAttributes)
                {
                    string temperature_day = reader.GetAttribute("day");
                    TextBoxTemp.Text = temperature_day;
                }
            }
        }
    }

I've tested the (URL) with a very simple XmlReader code bit and have confirmed it to work. I'm completely out of ideas now, the attribute INSIDE an element is screwing me over.


Solution

  • Use XDocument instead of XmlReader much easier to query.

    first example

    some code where you have too loop thru the results that did return from the LINQ query. But since you only have one < time > element and one < temperature > element inside that you can better use the second example

           // Create XDocument and load in the file
            XDocument _doc = XDocument.Load("C:\\t\\My File2.txt");
            //select all time element where attribute day.Value == to any day you give it just change "2017-04-18" 
            var time = _doc.XPathSelectElements("/weatherdata/forecast/time").Where(c => c.Attribute("day").Value == "2017-04-18");
            // Get IEnumerable of temperatures in the time element
            var temp = time.Select(x => x.Element("temperature"));
    
            // loop temperatures and get the attribute value from the element
            foreach (var element in temp)
            {
                string day = element.Attribute("day").Value;
                string min = element.Attribute("min").Value;
                string max = element.Attribute("max").Value;
                string night = element.Attribute("night").Value;
                string eve = element.Attribute("eve").Value;
                string morn = element.Attribute("morn").Value;
                MessageBox.Show($"day:{day},\nmin:{min},\nmax:{max},\nnight:{night},\neve:{eve},\nmorn:{morn}");
            }
    

    Second example

    I get the first one of a query result, so you don't have to loop to get one element. It is a bit riskier because you can get a null reference exception when no elements are found. but by a simple if you can catch it before it will be thrown.

             // Create XDocument and load in the file my case it is the path "C:\\t\\My File2.txt"
            XDocument _doc = XDocument.Load("C:\\t\\My File2.txt");
            // Select all time elements where attribute day.Value == to any day you give it, just change "2017-04-18" 
            // And select the first one of that result as I aspect there is only one time a day
            var time = _doc.XPathSelectElements("/weatherdata/forecast/time").Where(c => c.Attribute("day").Value == "2017-04-18").FirstOrDefault();
            // Get the children of time element by using .Elements() and take the first one of that.
            // You only have one temperature element in a time element so it will take that one with FirstOrDefault();
            var temp = time.Elements().FirstOrDefault();
            // assign attributes values to string
            string day = temp.Attribute("day").Value;
            string min = temp.Attribute("min").Value;
            string max = temp.Attribute("max").Value;
            string night = temp.Attribute("night").Value;
            string eve = temp.Attribute("eve").Value;
            string morn = temp.Attribute("morn").Value;
    
            // control
            MessageBox.Show($"day:{day},\nmin:{min},\nmax:{max},\nnight:{night},\neve:{eve},\nmorn:{morn}");
    

    Result with day "2017-04-18":

    enter image description here