mistergreen
mistergreen

Reputation: 109

How to select nodes contains in a variable?

XML is generated dynamically in a variable myvar. Then I'd like to select the Line nodes where MyDate != '99991231'.

This does not work:

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

  <xsl:variable name="myvar"> 
    <output>
      <Line><LineNumber>1</LineNumber><MyDate>99991231</MyDate><Amt>10</Amt></Line>
      <Line><LineNumber>2</LineNumber><MyDate>20150131</MyDate><Amt>15</Amt></Line>
      <Line><LineNumber>3</LineNumber><MyDate>99991231</MyDate><Amt>20</Amt></Line>
      <Line><LineNumber>4</LineNumber><MyDate>20161231</MyDate><Amt>30</Amt></Line>
      <Line><LineNumber>5</LineNumber><MyDate>99991231</MyDate><Amt>40</Amt></Line>
      <Line><LineNumber>6</LineNumber><MyDate>20171231</MyDate><Amt>50</Amt></Line>
      <Line><LineNumber>7</LineNumber><MyDate>20140131</MyDate><Amt>60</Amt></Line>
    </output>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:copy-of select="$myvar[Line/MyDate='99991231']"/>
  </xsl:template>

</xsl:stylesheet>

I'd like to obtain :

<output> 
  <Line><LineNumber>2</LineNumber><MyDate>20150131</MyDate><Amt>15</Amt></Line>
  <Line><LineNumber>4</LineNumber><MyDate>20161231</MyDate><Amt>30</Amt></Line>
  <Line><LineNumber>6</LineNumber><MyDate>20171231</MyDate><Amt>50</Amt></Line>
  <Line><LineNumber>7</LineNumber><MyDate>20140131</MyDate><Amt>60</Amt></Line>
</output>

Any hint ?

Upvotes: 1

Views: 90

Answers (3)

zx485
zx485

Reputation: 29022

Providing a pure and working XSLT-1.0 solution is not that easy because of the Resulting-Tree-Fragment problem with the variable.

So I wrapped the data inside a data island instead of a variable and used the method from here to avoid the namespace being copied by <copy-of>.

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:var="http://some.var" exclude-result-prefixes="var">
<xsl:output method="xml" indent="yes" />

<var:var>
  <output>
    <Line><LineNumber>1</LineNumber><MyDate>99991231</MyDate><Amt>10</Amt></Line>
    <Line><LineNumber>2</LineNumber><MyDate>20150131</MyDate><Amt>15</Amt></Line>
    <Line><LineNumber>3</LineNumber><MyDate>99991231</MyDate><Amt>20</Amt></Line>
    <Line><LineNumber>4</LineNumber><MyDate>20161231</MyDate><Amt>30</Amt></Line>
    <Line><LineNumber>5</LineNumber><MyDate>99991231</MyDate><Amt>40</Amt></Line>
    <Line><LineNumber>6</LineNumber><MyDate>20171231</MyDate><Amt>50</Amt></Line>
    <Line><LineNumber>7</LineNumber><MyDate>20140131</MyDate><Amt>60</Amt></Line>
  </output>
</var:var>

<xsl:template match="/">
  <output>
    <xsl:apply-templates select="document('')/xsl:stylesheet/var:var/output/Line[MyDate != '99991231']"/>
  </output>
</xsl:template>

<!-- Copy elements - solution from the second SO answer -->
<xsl:template match="*" priority="-1">
   <xsl:element name="{name()}">
      <xsl:apply-templates select="node()|@*"/>
   </xsl:element>
</xsl:template>

<!-- Copy all other nodes -->
<xsl:template match="node()|@*" priority="-2">
   <xsl:copy />      
</xsl:template>

</xsl:stylesheet>

This gives the desired output:

<?xml version="1.0"?>
<output>
  <Line>
    <LineNumber>2</LineNumber>
    <MyDate>20150131</MyDate>
    <Amt>15</Amt>
  </Line>
  <Line>
    <LineNumber>4</LineNumber>
    <MyDate>20161231</MyDate>
    <Amt>30</Amt>
  </Line>
  <Line>
    <LineNumber>6</LineNumber>
    <MyDate>20171231</MyDate>
    <Amt>50</Amt>
  </Line>
  <Line>
    <LineNumber>7</LineNumber>
    <MyDate>20140131</MyDate>
    <Amt>60</Amt>
  </Line>
</output>

Upvotes: 2

michael.hor257k
michael.hor257k

Reputation: 116959

Since you seem to be using XSLT 1.0, you should try it this way:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>

<xsl:variable name="myvar"> 
    <Line><LineNumber>1</LineNumber><MyDate>99991231</MyDate><Amt>10</Amt></Line>
    <Line><LineNumber>2</LineNumber><MyDate>20150131</MyDate><Amt>15</Amt></Line>
    <Line><LineNumber>3</LineNumber><MyDate>99991231</MyDate><Amt>20</Amt></Line>
    <Line><LineNumber>4</LineNumber><MyDate>20161231</MyDate><Amt>30</Amt></Line>
    <Line><LineNumber>5</LineNumber><MyDate>99991231</MyDate><Amt>40</Amt></Line>
    <Line><LineNumber>6</LineNumber><MyDate>20171231</MyDate><Amt>50</Amt></Line>
    <Line><LineNumber>7</LineNumber><MyDate>20140131</MyDate><Amt>60</Amt></Line>
 </xsl:variable>

<xsl:template match="/">
    <output>
        <xsl:copy-of select="exsl:node-set($myvar)/Line[MyDate != '99991231']"/>
    </output>
</xsl:template>

</xsl:stylesheet>

Upvotes: 1

kjhughes
kjhughes

Reputation: 111491

XSLT 2.0 Solution

To copy from $myvar those Line elements whose MyDate child does not equal 99991231:

<xsl:copy-of select="$myvar/output/Line[MyDate!='99991231']"/>

You'll also have to wrap the selected elements in a common element to generate well-formed XML.

Upvotes: 2

Related Questions