C# extension where method - handling a case when element not found
I am trying to query this very complicated XML document using xDocument and LINQ to XML. I want to perform the following action:
Get all elements that answer to a certain criteria, and if they don't, return another attribute from the xDocument.
Example:
<cars>
<car>
&l开发者_JAVA技巧t;patrol type="oil">
<url> http://Toyotaoil.com </url>
</patrol>
</car>
<car>
<patrol type="oil">
<url> http://BMWoil.com </url>
</patrol>
<patrol type="gas">
<url> http://BMWgas.com </url>
</patrol>
</car>
<car>
<patrol type="gas">
<url> http://Hondagas.com </url>
</patrol>
</car>
Now what I'd like to get from this query is a list of patrols of type oil, unless the car doesn't use petrol, and then I'd be satisfied with gas.
If I use the where
clause I just miss the cases where the car uses gas. Is there any such thing like a where
clause, where I can specify what to do if they condition wasn't met?
The solution below should give you the flexibility to query whatever you like:
var result = from car in xdoc.Element("cars").Elements("car")
let patrols = car.Elements("patrol")
let oils = patrols.Where(patrol => patrol.Attribute("type") == "oil")
select new {
Car = car,
Patrols = (oils.Any() ? oils : patrols)
}
I don't have Visual Studio here, so I hope it compiles :)
Give a bit more information on what you like to select, and I'll give you a more specific LINQ statement.
xdoc.Element("cars")
.Elements("car")
.Select(car => car.Elements("patrol")
.SingleOrDefault(p => (string)p.Attribute("type") == "oil")
??
car.Elements("patrol")
.Single(p => (string)p.Attribute("type") == "gas"));
You can just make something like this:
var query = from element in someElements
select element.Attribute("type").Value == "oil"
? returnSomethingWhenItIsOil
: returnSomethingWhenItIsSomethingElse;
or
var query = from element in someElements
where element.Attribute("type") == "oil"
|| element.Attribute("type") == "gas"
select element;
But explain the problem better, thanks :)
It sounds like you don't actually want a where clause at all - you just want a select clause that either picks one value or another.
However, your example doesn't really describe how you'd select a different item based on the values - which "other attribute" would you select? What do you mean by "where the car uses gas"? If you can give more details of the example, it shouldn't be too hard to give you matching code.
var cars = from c in xdoc.Descendants("car")
where
(c.Element("patrol").Attribute("type").Value == "oil" ||
c.Element("patrol").Attribute("type").Value == "gas")
select new Car
{
FuelType = c.Element("patrol").Attribute("type").Value.ToString()
};
foreach (Car c in cars)
{
Console.WriteLine(c.ToString());
}
class Car
{
public string FuelType { get; set; }
public override string ToString()
{
return "Car FeulType = " + this.FuelType.ToString();
}
}
These are the results, I am getting
alt text http://img694.imageshack.us/img694/5016/carse.jpg
Order
by type according to your specifications, take FirstOrDefault()
.
Edit: What I meant was something like this:
var patrols = from car in doc.Root.Elements()
let p = car.Elements().OrderBy(patrol=>patrol.Attribute("type").Value).First()
select p;
This returns one result per car. Check the OrderBy
clause and adjust it accordingly. This would result in:
<patrol type="oil">
<url> http://Toyotaoil.com </url>
</patrol>
<patrol type="gas">
<url> http://BMWgas.com </url>
</patrol>
<patrol type="gas">
<url> http://Hondagas.com </url>
</patrol>
Edit again: Ah, now it clicked. Yes, this only returns a single item per car - Zyphrax gave a nice solution.
精彩评论