开发者

Extract value of an element where an attribute equals a specific value

I'm stumped yet again by XDocument. I'm trying to extract the value of the temperature element (12 in this example) when the class attribute has a value="high" (and "low")

A subset of my XML:

<forecastGroup>
    <forecast>
      <temperatures>
          <textSummary>Low plus 2. High 12.</textSummary>
          <temperature unitType="metric" units="C" class="high">12</temperature>
          <temperature unitType="metric" units="C" class="low">2</temperature>
      </temperatures>
    </forecast>
    ...etc.
    <forecast>
          <temperature unitType="metric" units="C" class="high">15</temperature>
          <temperature unitType="metric" units="C" class="low">3</temperature>
    </forecast>
<forecastGroup>

Code so far:

XDocument loaded = XDocument.Parse(strInputXML);
foreach (var forecast in loaded.Descendants("forecastGroup").Elements("forecast"))
{
   //existing code doing stuff here using the XDocument loaded
   High开发者_运维问答 = "this is where I'm lost";
}

I've seemingly tried every combinations of trying to select Elements, Attributes, and Descendants", but I'm at a loss.


You can just add a Where() filter to your Linq to XML query:

XDocument loaded = XDocument.Parse(strInputXML);
var matchingForecasts = loaded.Descendants("temperature")
                              .Where(x => (string)x.Attribute("class") == "high");
foreach (var forecast in matchingForecasts)
{
    //do something
    string temperature = forecast.Value;
}

Alternatively you could look at each class attribute value within the foreach loop, which is closer to your original approach:

foreach (var forecast in loaded.Descendants("temperature"))
{
    //existing code doing stuff here using the XDocument loaded
    if (forecast.Attribute("class").Value == "high")
    {
        //do something
        string temperature = forecast.Value;
    }
}


To extract the high inside your loop, you could use the line

var high = (int)forecast.Element("temperatures")
                        .Elements("temperature")
                        .Where(temp => temp.Attribute("class").Value == "high")
                        .First();

Of course, you could use Linq-to-XML to simply project the entire XML tree into an appropriate object graph without explicitly taking it apart in a loop, but you should be able to progress your way towards that. It may end up looking something like

var forecasts = from forecast in loaded.Descendants("forecast")
                let temps = forecast.Element("temperatures")
                let high = temps.Elements("temperature").Where(t => t.Attribute("class").Value == "high").First()
                let low = temps.Elements("temperature").Where(t => t.Attribute("class").Value == "low").First()
                select new
                {
                    Temperatures = new
                    {
                        Summary = temps.Element("textSummary").Value,
                        High = new
                        {
                            UnitType = high.Attribute("unitType").Value,
                            Units = high.Attribute("units").Value,
                            Value = (int)high
                        },
                        Low = new
                        {
                            UnitType = low.Attribute("unitType").Value,
                            Units = low.Attribute("units").Value,
                            Value = (int)low
                        },
                    }
                };


loaded.Descendants("temperature")
.Where(d => d.Attribute("class").Value.Equals("high")).First().Value


The sample XML file will not work because it is not closed properly.

 <forecastGroup>
    <forecast>
      <temperatures>
          <textSummary>Low plus 2. High 12.</textSummary>
          <temperature unitType="metric" units="C" class="high">12</temperature>
          <temperature unitType="metric" units="C" class="low">2</temperature>
      </temperatures>
    </forecast>
    ...etc.
    <forecast>
          <temperature unitType="metric" units="C" class="high">15</temperature>
          <temperature unitType="metric" units="C" class="low">3</temperature>
    </forecast>
<forecastGroup>   // <= this needs to be </forecastGroup>


You can try use XPath like this:

using System.Xml.XPath;
...
string xpathExpression = "forecastGroup/forecast//temperature[@class='high']";
foreach (XElement el in loaded.XPathSelectElements(xpathExpression))
{
    int highTemperature = Int32.Parse(el.Value);
}

The search expression could be shorter ("//temperature[@class='high']"), but it's more efficient to be more detailed about position of values.

If you want to filter temperatures with 'high' or 'low' class attribute value, you can use this xpath expression:

"forecastGroup/forecast//temperature[@class='high' or @class='low']"

If you want decide what to do, based on @class attribute, you can use this code:

string xpathExpression = "forecastGroup/forecast//temperature[@class='high' or @class='low']";
foreach (XElement el in loaded.XPathSelectElements(xpathExpression))
{
    int temperature = Int32.Parse(el.Value);
    if (el.Attribute("class").Value == "low")
    {
        // do sth with low value
    }
    else
    {
        // do sth with high value
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜