2

I would like to compare two XML-files via XSLT. The comparison should be considered to be successful if all elements of a specific type in document 1 are located at the same XPath position in document 2.

Consider

<entry>
    <entry1>
        <entry2>
            <value type="1"/>
        </entry2>
    </entry1>
</entry>

as document 1.

The element under observation is "value" (with attribute type=1) which is located at entry/entry1/entry2. Therefore a comparison in this sense to

<entry>
    <entry0/>
    <entry0/>
    <entry1>
        <entry2>
            <value type="1"/>
        </entry2>
    </entry1>
</entry>

should be considered as successful, while

<entry>
    <entry1>
        <value type="1"/>
    </entry1>
</entry>

is not successful, since "value" (with attribute type=1) is located at entry/entry1. Also the comparison to

<entry>
    <entry1>
        <entry2>
            <value type="2"/>
        </entry2>
    </entry1>
</entry>

should be considered as not successful since the attribute of value is type=2.

My naive trial to fulfill this task in XSLT was something like:

<xsl:template match="value">
   <xsl:if test="not(document($doc2)/.[@type=@type])">
      <xsl:text>something is missing</xsl:text>
   </xsl:if>
</xsl:template>

This approach wasn't successful because the selection of the desired XPath within the 2nd document seems not to work.

Maybe you have an idea on how to address this question?

Matt

Matt
  • 23
  • 3
  • If you need to test only a few elements probably the best way is to write testing XPaths by hand, execute them against different files and compare results. Otherwise I don't know simple solution to this problem. – Alex Nikolaenkov Mar 10 '11 at 13:41
  • I don't understand your comparison. ` ` is very different from `entry>` . Why should the comparison between them be successful? – Dimitre Novatchev Mar 10 '11 at 13:45
  • Ok, don't call it "comparison" but "check, whether every element under observation in doc1 is present in doc2 at the same XPath". I'm going to try deep-equal as proposed in the answer below. – Matt Mar 10 '11 at 14:36

2 Answers2

3

Your question is hopelessly underspecified. For example, as well as requiring every element in doc1 to have a corresponding element in doc2, do you also require every element in doc2 to have a corresponding element in doc1?

However, something close might be the condition "for every element V1 in D1 such that name(V1)=N, there exists an element V2 in D2 such that name(V2)=N and deep-equal(V1, V2) and path(V1) = path(V2), where path($V) is defined as string-join($V/ancestor-or-self::*/name())", Which translates into the following XPath 2.0 expression:

every $V1 in $D1//N satisfies
some $V2 in $D2//N satisfies
deep-equal($V1, $V2) and
string-join($V1/ancestor-or-self::*/name())
 = string-join($V2/ancestor-or-self::*/name())  
Michael Kay
  • 156,231
  • 11
  • 92
  • 164
  • Sorry if you think my question is underspecified, I just tried to focus on the main aspect. And especially your example is not relevant: doc2 may include tons of other elements that won't affect the comparison in my sense (so may be the word "comparison" is misleading - sorry for that!) Anyway, thank you for the idea which indeed is helpful. – Matt Mar 10 '11 at 14:28
0

Just for fun, an XSLT 1.0 translation of Dr. Kay's answer:

    <xsl:variable name="vTest1">
        <xsl:for-each select="$D1//value[@type]">
            <xsl:variable name="vPath1">
                <xsl:for-each select="ancestor-or-self::*">
                    <xsl:value-of select="concat('/',name())"/>
                </xsl:for-each>
            </xsl:variable>
            <xsl:variable name="vTest2">
                <xsl:for-each select="$D2//value[@type=current()/@type]">
                    <xsl:variable name="vPath2">
                        <xsl:for-each select="ancestor-or-self::*">
                            <xsl:value-of select="concat('/',name())"/>
                        </xsl:for-each>
                    </xsl:variable>
                    <xsl:if test="$vPath1=$vPath2">True</xsl:if>
                </xsl:for-each>
            </xsl:variable>
            <xsl:if test="$vTest2=''">False</xsl:if>
        </xsl:for-each>
    </xsl:variable>

Then $vTest1 = '' would be the boolean value of the test.

Community
  • 1
  • 1