Omar Kooheji
Omar Kooheji

Reputation: 55770

Can you filter an xml document to a subset of nodes using XPath in C#?

I'm trying to filter an Xml document so into a subset of itself using XPath.

I have used XPath get an XmlNodeList, but I need to transform this into an XML document.

Is there a way to either transform an XMLNodeList into an XmlDocument or to produce an XmlDocument by filtering another XmlDocument directly?

Upvotes: 2

Views: 3672

Answers (3)

Robert Rossney
Robert Rossney

Reputation: 96830

This is a pretty typical reason to use XSLT, which is an efficient and powerful tool for transforming one XML document into another (or into HTML, or text).

Here's a minimal program to perform an XSLT transform and send the results to the console:

using System;
using System.Xml;
using System.Xml.Xsl;

namespace XsltTest
{
    class Program
    {
        static void Main(string[] args)
        {
            XslCompiledTransform xslt = new XslCompiledTransform();
            xslt.Load("test.xslt");

            XmlWriter xw = XmlWriter.Create(Console.Out);
            xslt.Transform("input.xml", xw);
            xw.Flush();
            xw.Close();

            Console.ReadKey();
        }
    }
}

Here's the actual XSLT, which is saved in test.xslt in the program directory. It's pretty simple: given an input document whose top-level element is named input, it creates an output element and copied over every child element whose value attribute is set to true.

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="xml" indent="yes"/>

    <xsl:template match="/input">
      <output>
        <xsl:apply-templates select="*[@value='true']"/>
      </output>
    </xsl:template>

    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

And here's input.xml:

<?xml version="1.0" encoding="utf-8" ?>
<input>
  <element value="true">
    <p>This will get copied to the output.</p>
    <p>Note that the use of the identity transform means that all of this content
    gets copied to the output simply because templates were applied to the 
    <em>element</em> element.
  </p>
  </element>
  <element value="false">
    <p>This, on the other hand, won't get copied to the output.</p>
  </element>
</input>

Upvotes: 1

Marc Gravell
Marc Gravell

Reputation: 1063338

With XmlDocument, you will need to import those nodes into a second document;

    XmlDocument doc = new XmlDocument();
    XmlElement root = (XmlElement)doc.AppendChild(doc.CreateElement("root"));
    XmlNodeList list = // your query
    foreach (XmlElement child in list)
    {
        root.AppendChild(doc.ImportNode(child, true));
    }

Upvotes: 4

Omar Kooheji
Omar Kooheji

Reputation: 55770

I've actualy just thought of a way to do this but it doesn't seem very elegant.

use a StringBuilder to combine the OuterXml of each of the XmlNodes in the XmlNodeList...

As I said it's inelegant but I think it might work. I'd appreciate any other suggestion...

Upvotes: 0

Related Questions