How to select some out of many nodes in XML along with the text or attribute values of that node

I have a complex XML , simplified version of which is given here. What I am trying to achieve is to get the data ( text or attribute values ) from only given set of nodes in an XML. Many examples are available only with XPATH. but my XML structure is complex and I wanted to use recursive method to scan through the nodes instead of hard coding XPATH.

INPUT:

<Root>
    <Book>
    <Content >
    <Chapter id="1" startpage="1" endpage="20">
        <Topic id="1">
        <Title>Title1</Title>
        <Content>Paragraphs</Content>
        </Topic>
        <Topic id="1.1">
        <Title>Title1.1</Title>
        <Content>Paragraphs</Content>
        </Topic>
        <Topic id="1.2">
        <Title>Title1.2</Title>
        <Content>Paragraphs</Content>
        </Topic>
    </Chapter>
    <Chapter id="2" startpage="21" endpage="90">
        <Topic id="2">
        <Title>Title2</Title>
        <Content>Paragraphs</Content>
        </Topic>
        <Topic id="2.1">
        <Title>Title2.1</Title>
        <Content>Paragraphs</Content>
        </Topic>
        <Topic id="2.1.2">
        <Title>Title2.1.2</Title>
        <Content>Paragraphs</Content>
        </Topic>
    </Chapter>
    <Index>
    Some more nodes here
    </Index>
             </Content>
    </Book>
</Root>

DESIRED OUTPUT:

<Root>
<Book>
<Content>
    <Chapter id="1" startpage="1" endpage="20">

    <Title>Title1</Title>

    <Title>Title1.1</Title>

    <Title>Title1.2</Title>

    </Chapter>
<Chapter id="2" startpage="21" endpage="90">

    <Title>Title2</Title>

    <Title>Title2.1</Title>

    <Title>Title2.1.2</Title>

</Chapter>

</Book>
</Root>

CURRENT XSL:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns="some:ns">
   <xsl:output omit-xml-declaration="yes" indent="yes"/>
   <xsl:strip-space elements="*"/>

   <ns:WhiteList>
      <name>Root</name>
      <name>Book</name>
      <name>Chapter</name>
      <name>Title</name> 
   </ns:WhiteList>

   <xsl:variable name="whistList" select="document('')/*/ns:WhiteList" />

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

   <xsl:template match="@*">
      <xsl:if test="attribute::*[name()=$whistList/*]">
         <xsl:copy/>

      </xsl:if>
   </xsl:template>   

     <xsl:template match="*">
      <xsl:if test="descendant-or-self::*[name()=$whistList/*]">
         <xsl:copy>
             <xsl:value-of select="node()" />
            <xsl:apply-templates select="@*|node()"/>
         </xsl:copy>

      </xsl:if>
   </xsl:template>
</xsl:stylesheet>

Answers


If the only determining factor is the node's name (regardless of the path to the node), you could try:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common">

<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:variable name="whiteList">
    <name>Root</name>
    <name>Book</name>
    <name>Chapter</name>
    <name>Title</name>
    <name>id</name>
</xsl:variable>
<xsl:variable name="whiteListSet" select="exsl:node-set($whiteList)/name" />

<xsl:template match="*">
    <xsl:choose>
        <xsl:when test="name()=$whiteListSet">
            <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
            </xsl:copy>
        </xsl:when>
        <xsl:otherwise>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

<xsl:template match="@*">
    <xsl:if test="name()=$whiteListSet and name(parent::*)=$whiteListSet">
        <xsl:copy/>
    </xsl:if>
</xsl:template>

<xsl:template match="text()">
    <xsl:if test="name(parent::*)=$whiteListSet">
        <xsl:copy/>
    </xsl:if>
</xsl:template>

</xsl:stylesheet>

When the above is applied to your example input, the result is:

<?xml version="1.0" encoding="UTF-8"?>
<Root>
   <Book>
      <Chapter id="1">
         <Title>Title1</Title>
         <Title>Title1.1</Title>
         <Title>Title1.2</Title>
      </Chapter>
      <Chapter id="2">
         <Title>Title2</Title>
         <Title>Title2.1</Title>
         <Title>Title2.1.2</Title>
      </Chapter>
   </Book>
</Root>

i have added the following to your stylesheet:

<xsl:template match="Topic">
    <xsl:apply-templates/>
</xsl:template>

and the output now is

<Root>
   <Book>
      <Content>
         <Chapter>
            <id>1</id>
            <startpage>1</startpage>
            <endpage>20</endpage>
            <Title>Title1</Title>
            <Title>Title1.1</Title>
            <Title>Title1.2</Title>
         </Chapter>
         <Chapter>
            <id>2</id>
            <startpage>21</startpage>
            <endpage>90</endpage>
            <Title>Title2</Title>
            <Title>Title2.1</Title>
            <Title>Title2.1.2</Title>
         </Chapter>
      </Content>
   </Book>
</Root>

Looks like this one is working. However, it still shows the node which i dont want. I believe that is because of the node having an attribute in it.

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns="some:ns">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>
    <ns:WhiteList>
            <elename>Chapter</elename>
            <elename>Title</elename>
    </ns:WhiteList>
    <xsl:variable name="whistList" select="document('')/*/ns:WhiteList" />

    <xsl:template match="node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="@*">
        <xsl:copy>
            <xsl:call-template name="flatattr"/>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="@*" name="flatattr">
        <xsl:element name="{local-name()}">
            <xsl:value-of select="."/>
        </xsl:element>
    </xsl:template>
    <xsl:template match="*">
        <xsl:if test="descendant-or-self::*[name()=$whistList/*]">
            <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
            </xsl:copy>
        </xsl:if>
    </xsl:template>
    <xsl:template match="Expression[ancestor::Expression]">
        <xsl:apply-templates />
    </xsl:template>
    <xsl:template match="SimpleExpression">
        <xsl:text>(</xsl:text>
        <xsl:apply-templates />
        <xsl:text>)</xsl:text>
    </xsl:template>
    <xsl:template match="ValueExpression|Operator">
        <xsl:text>(</xsl:text>
        <xsl:value-of select="." />
        <xsl:text>)</xsl:text>
    </xsl:template>
</xsl:stylesheet>

Need Your Help

I need to execute a user space application every time a USB device is connected

c linux-kernel driver embedded-linux kernel-module

I am working with an embedded platform. Typical software in this devices are Linux 2.6 + Busybox, so resources are limited.

Auto Layout (Constraints) Center 2 side by side views in a parent view

iphone ios constraints autolayout

I'm trying to figure out how to do this with auto layout (iOS6) and constraints.