XSLT string compare

Compare

1. > What is the proper syntax for a test to compare strings
2. Compare data from different XML-files
3. Comparing two XML documents
4. comparing two xpath fragment | node-sets

1.

> What is the proper syntax for a test to compare strings

David Carlisle


Q expansion
<Whatever>
                <Choice>
                    true
                </Choice>
</Whatever>
            
>And I want to output one of two values, depending on whether the
>Choice element is true or false.
	

Note that the content is not just 'true' but has white space, so you want

	  <xsl:when test="normalize-space(Choice)='true')">
            

2.

Compare data from different XML-files

Jeni Tennison

> I want to compare XML-data from two different XML-sources-files in my
> XSL-stylesheet, how do I do that? 
	

The function that you need is the document() function, which pulls in information from a named XML file. The way I would usually arrange this is to have the input to the XSLT stylesheet be some XML that describes the position of the two files that you want to compare:

- --- input.xml ---
<files>
  <file href="file1.xml" />
  <file href="file2.xml" />
</files>
- ---

You can then access the two documents and put them into global variables using:

<xsl:variable name="file1"
              select="document(/files/file[1]/@href, /)" />
<xsl:variable name="file2"
              select="document(/files/file[2]/@href, /)" />

[Note the second argument to document() ensures that the file names are resolved relative to your input.xml document rather than the stylesheet.]

An alternative way of reaching this state is to use one of the files as the input to the stylesheet, and name the other as a parameter to the stylesheet. You can then use:

<xsl:param name="comparison-file" />
<xsl:variable name="file1" select="/" />
<xsl:variable name="file2" select="document($comparison-file, /)" />

When you have the two document trees as separate variables, you can compare them however you want. For example, to test whether the names of the document elements are the same you could use:

  name($file1/*) = name($file2/*)

3.

Comparing two XML documents

Lars Huttar

So anyway, here's a tail-recursive solution to the problem of comparing two XML documents that works.

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

  <!-- Replace // with / everywhere if we're only interested
   	in immediate children of /RootElement. -->

  <xsl:variable name="docA" select="/" />
  <xsl:variable name="docB" select="document('try-compare2.xml')"/>

  <!-- This produces a whole nother copy of both docs!
       So, is the performance cost worth it?? -->

  <xsl:variable name="sortedNodesA">
    <!-- produce a sorted, flattened RTF of A's nodes -->
    <xsl:for-each select="$docA/RootElement//*">
      <xsl:sort select="name()" />
      <xsl:copy-of select="." />
    </xsl:for-each>
  </xsl:variable>

  <xsl:variable name="sortedNodesB">
    <!-- produce a sorted, flattened RTF of B's nodes -->
    <xsl:for-each select="$docB/RootElement//*">
      <xsl:sort select="name()" />
      <xsl:copy-of select="." />
    </xsl:for-each>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:call-template name="recurse">
      <xsl:with-param name="nodesA"
        select="exslt:node-set($sortedNodesA)/*" />
      <xsl:with-param name="nodesB"
        select="exslt:node-set($sortedNodesB)/*" />
    </xsl:call-template>
  </xsl:template>
 
  <xsl:template name="recurse">
    <xsl:param name="nodesA" />
    <xsl:param name="nodesB" />
    <xsl:if test="$nodesA | $nodesB">
      <xsl:variable name="nameA" select="name($nodesA[1])" />
      <xsl:variable name="nameB" select="name($nodesB[1])" />
      <xsl:variable name="compar">
        <xsl:call-template name="compare-names">
          <xsl:with-param name="a" select="$nodesA[1]" />
          <xsl:with-param name="b" select="$nodesB[1]" />
        </xsl:call-template>
      </xsl:variable>

      <xsl:choose>
        <xsl:when test="0 > $compar"> <!-- $nodesA[1] is alph. first -->
          <p><xsl:value-of select="$nameA" /> is only in doc A.</p>
          <xsl:call-template name="recurse">
            <xsl:with-param name="nodesA" select="$nodesA[position()>1]" />
            <xsl:with-param name="nodesB" select="$nodesB" />
          </xsl:call-template>
        </xsl:when>

        <xsl:when test="$compar > 0"> <!-- $nodesB[1] is alph. first -->
          <p><xsl:value-of select="$nameB" /> is only in doc B.</p>
          <xsl:call-template name="recurse">
            <xsl:with-param name="nodesA" select="$nodesA" />
            <xsl:with-param name="nodesB" select="$nodesB[position()>1]" />
          </xsl:call-template>
        </xsl:when>

        <xsl:otherwise>
          <p><xsl:value-of select="$nameB" /> is in both documents.
            <!-- Do I need string(text(...))? -->
            <xsl:if
              test="string($nodesA[1]/text()) != string($nodesB[1]/text())">
              But their contents differ:
              '<xsl:value-of select="$nodesA[1]/text()" />' !=
              '<xsl:value-of select="$nodesB[1]/text()" />'.
            </xsl:if>
          </p>
          <xsl:call-template name="recurse">
            <xsl:with-param name="nodesA" select="$nodesA[position()>1]" />
            <xsl:with-param name="nodesB" select="$nodesB[position()>1]" />
          </xsl:call-template>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:if>
  </xsl:template>

  <xsl:template name="compare-names">
    <!-- Output -1, 0, or 1 as name of node A sorts before, equal to,
         or after name of node B. -->
    <xsl:param name="a" />
    <xsl:param name="b" />
    <xsl:choose>
      <xsl:when test="name($a) = name($b)"> 0 </xsl:when>
      <xsl:otherwise>
        <xsl:for-each select="$a|$b">
          <xsl:sort select="name()" />
          <xsl:if test="position() = 1">
            <xsl:choose>
              <xsl:when test="name(.) = name($a)"> -1 </xsl:when>
              <xsl:otherwise> 1 </xsl:otherwise>
            </xsl:choose>
          </xsl:if>
        </xsl:for-each>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

4.

comparing two xpath fragment | node-sets

Michael Kay



> I mean that the two xpaths are  equivalent in the sense that they 
> produce the same results when evaluated against a particular source 
> document.

OK, so you're not comparing two XPath expressions, you are comparing the node-sets produced when they are evaluated.

Simplest test is

   count(a) = count(b) and count(a) = count(a|b)