Query help
I have to live with strangely formatted XML file. I would like to know if this is possible to query with LINQ and how.So I would like to get Vendor, Model and other attributes out from that. With my skills I can only get one attribute and then I'm in dead end.
Problem is that I need to check one attribue value in element in order to query other attribue value in same element.
I couldn't find this kind of example anywhere.
<car name="Toyota Coupe">
<profile name="Vendor" value="Toyota"/>
<profile name="Model" value="Celica"/>
<profile name="Doors" value="2"/>
<support name="Racing" value="yes"/>
<support name="Towing" value="no"/>
</car>
<car name="Honda Accord Aerodec">
<profile name="Vendor" value="Honda"/>
<profile name="Model" value="Accord"/>
<profile name="Doors" value="4"/>
<support name="Racing" value="no"/>
<support name="Towing" value="yes"/>
</car>
var result = from e in XElement.Load(@"x.xml").Elements("car")
where e.Element("profile").Attribute("name").Value == "Vendor"
select new Car
{
Vendor=(string)e.Attribute("value"),
//Model=
//Doors=
//RacingSupport=
};
Answers
- Here is how you can do it:
It's easier to isolate the "profile" and "support" elements in separate sequences using let clauses, as above. Then the select clause becomes simpler.Code Blockvar result =
from car in root.Elements("car")
let profiles =
from profile in car.Elements("profile")
select new {Name = profile.Attribute("name").Value,
Value = profile.Attribute("value").Value
}
let supports =
from support in car.Elements("support")
select new {Name = support.Attribute("name").Value,
Value = support.Attribute("value").Value
}
select new Car {
Name = car.Attribute("name").Value,
Vendor = profiles.Single(prof => prof.Name == "Vendor").Value,
Model = profiles.Single(prof => prof.Name == "Model").Value,
Doors = int.Parse(profiles.Single(prof => prof.Name == "Doors").Value),
RacingSupport = supports.Single(sup => sup.Name == "Racing").Value == "yes"
};
More on my blog.
All Replies
- Here is how you can do it:
It's easier to isolate the "profile" and "support" elements in separate sequences using let clauses, as above. Then the select clause becomes simpler.Code Blockvar result =
from car in root.Elements("car")
let profiles =
from profile in car.Elements("profile")
select new {Name = profile.Attribute("name").Value,
Value = profile.Attribute("value").Value
}
let supports =
from support in car.Elements("support")
select new {Name = support.Attribute("name").Value,
Value = support.Attribute("value").Value
}
select new Car {
Name = car.Attribute("name").Value,
Vendor = profiles.Single(prof => prof.Name == "Vendor").Value,
Model = profiles.Single(prof => prof.Name == "Model").Value,
Doors = int.Parse(profiles.Single(prof => prof.Name == "Doors").Value),
RacingSupport = supports.Single(sup => sup.Name == "Racing").Value == "yes"
};
More on my blog. To simplify this a bit more, we can skip the let clauses and include the single method directly in the select cluse. Thus you can re-write this (in VB to really simplify it

Dim query = From car In source...<car> _ Select _Name = car.
@name, _Vendor = car.
<profile>.Single(Function(profile) profile.@name = "Vendor").@value, _Model = car.
<profile>.Single(Function(profile) profile.@name = "Model").@value, _Doors = car.
<profile>.Single(Function(profile) profile.@name = "Doors").@value, _RacingSupport = (car.
<support>.Single(Function(support) support.@name = "Racing").@value = "yes")This in essence says, take the car descendent elements of the source car XML element/document. Select the name attribute from that car. Also, for the vendor, select the profile child elements of the car and find the single one where the attribute "name" is "Vendor". From that element, select it's value attribute. The same process is applied for Model, Doors and RacingSupport.
Of course, if you can't ensure that your source will have all of the requisite child elements, you would need to handle nullability as necessary.
Jim Wooley

