Let's say I have the following doc
<sets version="2.0">
<setting>
<id>set1</id>
<value>80</value>
<label>EVersion</label>
<type>Val</type>
<format>R</format>
<bits>
<addr>0</addr>
<startBit>0</startBit>
<bitWidth>8</bitWidth>
</bitspec>
</setting>
<setting>
<id>set3</id>
<value>50</value>
<label>GVersion</label>
<type>Bin</type>
<format>R</format>
<bits>
<addr>0</addr>
<startBit>0</startBit>
<bitWidth>8</bitWidth>
</bitspec>
</setting>
</sets>
and I just want the ID and value elements -
<sets version="2.0">
<setting>
<id>set1</id>
<value>80</value>
</setting>
<setting>
<id>set3</id>
<value>50</value>
</setting>
</sets>
How could I select just these using XDocument and LINQ?
Use linq-to-xml, the following code:
var xml_str = @"<sets version=""2.0"">
<setting>
<id>set1</id>
<value>80</value>
<label>EVersion</label>
<type>Val</type>
<format>R</format>
<bits>
<addr>0</addr>
<startBit>0</startBit>
<bitWidth>8</bitWidth>
</bits>
</setting>
<setting>
<id>set3</id>
<value>50</value>
<label>GVersion</label>
<type>Bin</type>
<format>R</format>
<bits>
<addr>0</addr>
<startBit>0</startBit>
<bitWidth>8</bitWidth>
</bits>
</setting>
</sets>";
var doc = XDocument.Parse(xml_str);
var settings = new XElement("sets",
from setting in doc.Element("sets").Elements("setting")
select new XElement("setting", setting.Element("id"), setting.Element("value")));
Console.WriteLine(settings);
prints:
<sets>
<setting>
<id>set1</id>
<value>80</value>
</setting>
<setting>
<id>set3</id>
<value>50</value>
</setting>
</sets>
If you don't need transformed XML, but just, say, dictionary of id -> value
mapping, (storing value
as int
), you could do:
var doc = XDocument.Parse(xml_str);
var settings_dict = doc.Element("sets").Elements("setting").ToDictionary(s => s.Element("id").Value, s => Convert.ToInt32(s.Element("value").Value));
var xdoc = XDocument.Load(@"c:\myxml.xml");
var settings =
xdoc.Element("sets").Elements("setting")
.Select(s => new
{
Id = s.Element("id").Value,
Value = s.Element("value").Value
});
This would produce an IEnumerable
of an anonymous type, with properties Id
and Value
(both strings).
You could create your own Setting
type, and use that in the projection instead, also converting the 'value' string value to an integer if required.
My first answer was very similar to the other answers, and wasn't as clear, so I removed it. However, I thought it would be a good exercise to write some linq that would generate the filtered XML when the desired elements were at any arbitrary depth. This is what I came up with:
Func<XElement, XElement> f = null;
f = e => e.Name == "id" || e.Name == "value" ? e : //on a match, return the node
new[] { new XElement(e.Name, e.Elements().Select(f)) } //else recurse
.FirstOrDefault(y => y.Elements().Any()); //keeping subtrees with matches
XElement resultXmlElement = f(XDocument.Parse(yourXmlString).Root);