Home > front end >  XSLT match elements with text nodes that do not match the same text node from another element
XSLT match elements with text nodes that do not match the same text node from another element

Time:04-13

In this sample XML I want to match app elements where the text in the indexInfo element does not match the same text from the z_app element with the same name.

Since the indexInfo element from appname2 and z_appname2 both match then I don't want to include these. In this case I would only want app elements appname1 and z_appname1 returned since their indexInfo content is different.

I'm looking for a match expression that I can use for an XSLT template.

<root>
    <app name="appname1">
      <indexInfo>
This is the text for the indexes
      </indexInfo>
    </app>
    
    <app name="z_appname1">
      <indexInfo>
This is the text for the indexes
But slightly different
      </indexInfo>
    </app>
    
    <app name="appname2">
      <indexInfo>
This is the text for the indexes
      </indexInfo>
    </app>
    
    <app name="z_appname2">
      <indexInfo>
This is the text for the indexes
      </indexInfo>
    </app>
</root>

Desired output:

<root>
    <app name="appname1">
      <indexInfo>
This is the text for the indexes
      </indexInfo>
    </app>

    <app name="z_appname1">
      <indexInfo>
This is the text for the indexes
But slightly different
      </indexInfo>
    </app>
</root>

CodePudding user response:

You could use xsl:key to create a map matching all of the app elements, using the value of @name after "appname" and the value of the indexInfo as the lookup key.

Then, in a template match expression, apply a predicate to match any app in which selecting items by that key has more than one item, use an empty template to exclude that matching app from the output:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    
    <xsl:key name="app" match="app" use="concat(substring-after(@name, 'appname'), '|', indexInfo)"/>
    
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    
    <xsl:template match="app[key('app', concat(substring-after(@name, 'appname'), '|', indexInfo))[2]]"/>
    
</xsl:stylesheet>

CodePudding user response:

I'd do it this way:

XSLT 1.0

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

<xsl:key name="app-by-name" match="app" use="@name" />

<xsl:template match="/root">
    <xsl:copy>
        <xsl:for-each select="app[starts-with(@name, 'z_')]">
            <xsl:variable name="peer" select="key('app-by-name', substring-after(@name, 'z_'))" />
            <xsl:if test="indexInfo != $peer/indexInfo">
                <xsl:copy-of select=". | $peer"/>
            </xsl:if>
        </xsl:for-each>
    </xsl:copy>
</xsl:template>
    
</xsl:stylesheet>
  • Related