Matt
Matt

Reputation: 26971

Easiest way to filter elements out of an XML document in .NET

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?

Upvotes: 2

Views: 4861

Answers (3)

Marty Neal
Marty Neal

Reputation: 9543

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);

Upvotes: 0

Krizz
Krizz

Reputation: 11552

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));

Upvotes: 2

devdigital
devdigital

Reputation: 34349

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.

Upvotes: 2

Related Questions