Craig Johnstone
Craig Johnstone

Reputation: 193

XSLT 2.0: Does value exist in list of elements?

I have an XML document as follows:

<Document>
  <Countries>
    <Country>Scotland</Country>
    <Country>England</Country>
    <Country>Wales</Country>
    <Country>Northern Ireland</Country>
  </Countries>

  <Populations>
    <Population country="Scotland" value="5" />
    <Population country="England" value="53" />
    <Population country="Wales" value="3" />
    <Population country="Northern Ireland" value="2" />
    <Population country="France" value="65" />
    <Population country="" value="35" />
  </Populations>
</Document>

I am attempting to write an XSLT statement which will access all Population elements where its "country" attribute is blank OR its "country" attribute is NOT in /Document/Countries/Country

My XSLT looks as follows:

<xsl:variable name="countries" select="/Document/Countries/Country" />
<xsl:variable name="populations" select="/Document/Populations/Population" />

<xsl:variable name="populationsNotInList" select="$populations[(@country = '') OR (@country NOT IN $countries)" />

Can you help me fill in the 'NOT IN $countries' part of the $populationsNotInList variable?

I am essentially looking for an output of:

<Population country="France" value="65" />
<Population country="" value="35" />

Upvotes: 0

Views: 7608

Answers (2)

Mathias M&#252;ller
Mathias M&#252;ller

Reputation: 22617

If you do not necessarily need to store them in a variable, this is how you can access the relevant nodes.

<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

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

<xsl:strip-space elements="*"/>

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

<xsl:template match="Document">
   <xsl:for-each select="Populations/Population">
     <xsl:if test="@country = '' or not(../../Countries/Country[.=current()/@country])">
        <xsl:copy>
           <xsl:copy-of select="@*"/>
        </xsl:copy>
     </xsl:if>
   </xsl:for-each>
</xsl:template>

<xsl:template match="Country"/>

</xsl:stylesheet>

This gives you the following output (assuming correct input - your input XML is not correct):

<?xml version="1.0" encoding="UTF-8"?>

<Population country="France" value="65"/>
<Population country="" value="35"/>

Upvotes: 1

Martin Honnen
Martin Honnen

Reputation: 167651

Use <xsl:variable name="populationsNotInList" select="$populations[(@country = '') or not(@country = $countries)" /> or better define a key

<xsl:key name="country" match="Countries/Country" use="."/>

then you can do <xsl:variable name="populationsNotInList" select="$populations[(@country = '') or not(key('country', @country))" />

Upvotes: 2

Related Questions