Search code examples
xsltxslt-1.0exslt

XSLT 1.0 multi-passing


I have the following input xml:

<bookstores>
  <store>
    <name>Store 1</name>
    <books>
      <book>
        <title>Book 1</title>
        <author>Author 1</author>
        <year>2000</year>
        <price/>
      </book>
      <book>
        <title>Book 2</title>
        <author></author>
        <year>2001</year>
        <price/>
      </book>
    </books>
  </store>
  <store>
    <name>Store 3</name>
    <books>
      <book>
        <title>Book 1</title>
        <year>2012</year>
        <price/>
      </book>
    </books>
  </store>
</bookstores>

I need to get all stores that have books with identified authors, so the result should be:

<bookstores>
  <store>
    <name>Store 1</name>
    <books>
      <book>
        <title>Book 1</title>
        <author>Author 1</author>
        <year>2000</year>
        <price/>
      </book>
    </books>
  </store>
</bookstores>

I tried to use exslt:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exslt="http://exslt.org/common">
  <xsl:output method="xml" indent="yes" encoding="utf-8" />
  <xsl:strip-space elements="*" />

  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*" />
    </xsl:copy>
  </xsl:template>
  <xsl:template match="/">
    <xsl:variable name="firstPass">
      <xsl:call-template name="processing" />
    </xsl:variable>
    <xsl:apply-templates select="exslt:node-set($firstPass)" />
  </xsl:template>
  <xsl:template name="processing" match="bookstores/store/books/book[author[string()='']]" />
  <xsl:template match="bookstores/store/books/book[not(author)]" />
  <xsl:template match="bookstores/store[not(books/book)]" />

</xsl:stylesheet>
  1. Filter books with empty Author
  2. Filter books without tag Author
  3. Filter sotres without books with authors

but unfortunately I didn't get how to use it in a right way. How to use exslt with several match templates?


Solution

  • I think you can do it in one pass

      <xsl:template match="store[not(books/book[author[normalize-space()]])]"/>
    
      <xsl:template match="book[not(author[normalize-space()])]"/>
    

    that way the complete code is

    <xsl:stylesheet
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        version="1.0">
    
      <xsl:template match="@* | node()">
        <xsl:copy>
          <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
      </xsl:template>
    
      <xsl:template match="store[not(books/book[author[normalize-space()]])]"/>
    
      <xsl:template match="book[not(author[normalize-space()])]"/>
    
    </xsl:stylesheet>
    

    and gives the wanted output at https://xsltfiddle.liberty-development.net/3NzcBtk.