XSLT filter nodes based on given node list

2019-06-06 15:40发布

问题:

This may have already be answered somewhere, but I do not have right words to search it :

Lets say I have data file which has list of cities :

 <cities>
    <city abbr='A'>NameA</city>
    <city abbr='b'>NameB</city>
  </cities>

The cities list is long and I want to filter the cities based on abbr

[filter data]

   <skip>
     <abbr>A</abbr>
     <abbr>B</abbr>
   </skip>

How could I use the this filter data(in xml form) to skip some nodes from original data file , specifically how I can use in for-each loop e.g

 <xsl:template match="/">    
      <xsl:for-each select="not in skip list">
         ???
      </xsl:for-each>     
  </xsl:template>

I want to use the filter data internally within the XSLT file in the form of xml format as the list might get too long.What are the options to include the file within the xslt? Currently I am using SAXON sth like this.

java -jar /usr/local/liquibase/saxon/saxon9he.jar ./base/cities.xml ./templates/split_cities.xslt authorName=sakhunzai

This example is over simplification of original data

回答1:

You tagged your question with saxon tag so I assume you are using xslt 2.0.

You could make a variable holding values to be skipped

<xsl:variable name="skip">
    <abbr>A</abbr>
    <abbr>C</abbr>
</xsl:variable>

Then you could test attribute of nodes against this variable

<xsl:apply-templates select="cities/city[not(@abbr = $skip/abbr)]" />

So for input

<?xml version="1.0" encoding="UTF-8"?>
 <cities>
    <city abbr='A'>NameA1</city>
    <city abbr='B'>NameB1</city>
    <city abbr='C'>NameC1</city>
    <city abbr='A'>NameA2</city>
    <city abbr='B'>NameB2</city>
    <city abbr='C'>NameC2</city>
  </cities>

Following xslt

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

    <xsl:variable name="skip">
        <abbr>A</abbr>
        <abbr>C</abbr>
    </xsl:variable>

    <xsl:template match="/">
        <cities>
            <xsl:apply-templates select="cities/city[not(@abbr = $skip/abbr)]" />
        </cities>
    </xsl:template>

    <xsl:template match="city">
        <xsl:copy-of select="." />
    </xsl:template>
</xsl:stylesheet>

Produces output

<?xml version="1.0" encoding="UTF-8"?>
<cities xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <city abbr="B">NameB1</city>
    <city abbr="B">NameB2</city>
</cities>

EDIT:

It makes sense to store filter in external file. Let skip.xml be such file with structure

<?xml version="1.0" encoding="UTF-8"?>
<skip>
    <abbr>A</abbr>
    <abbr>C</abbr>
</skip>

Then you can change variable declaration in following manner

<xsl:variable name="skip" select="document('path/to/skip.xml')/skip/abbr" />

Other thing might stay unchanged.



标签: xml xslt saxon