Простое и точное решение XSLT :
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:apply-templates select="//@Caption"/>
</xsl:template>
<xsl:template match="@Caption">
<xsl:apply-templates select="." mode="path"/>
<xsl:value-of select="concat(': ',.,'
')"/>
</xsl:template>
<xsl:template match="@Caption" mode="path">
<xsl:for-each select="ancestor::*">
<xsl:value-of select="concat('/',name())"/>
<xsl:variable name="vSiblings" select=
"count(../*[name()=name(current())])"/>
<xsl:if test="$vSiblings > 1">
<xsl:value-of select="
concat('[',
count(preceding-sibling::*
[name()=name(current())]) +1,
']'
)"/>
</xsl:if>
</xsl:for-each>
<xsl:text>/@Caption</xsl:text>
</xsl:template>
</xsl:stylesheet>
когда это преобразование применяется к предоставленному документу XML :
<Test Description="Test XML" VersionFormat="123" ProtectedContentText="(Test test)">
<Testapp>
<TestappA>
<A Id="0" Caption="Test 0" />
<A Id="1" Caption="Test 1" />
<A Id="2" Caption="Test 2" />
<A Id="3" Caption="Test 3">
<AA>
<B Id="4" Caption="Test 4" />
</AA>
</A>
</TestappA>
<AA>
<Reason Id="5" Caption="Test 5" />
<Reason Id="6" Caption="Test 6" />
<Reason Id="7" Caption="Test 7" />
</AA>
</Testapp>
</Test>
желаемый, правильный результат получается :
/Test/Testapp/TestappA/A[1]/@Caption: Test 0
/Test/Testapp/TestappA/A[2]/@Caption: Test 1
/Test/Testapp/TestappA/A[3]/@Caption: Test 2
/Test/Testapp/TestappA/A[4]/@Caption: Test 3
/Test/Testapp/TestappA/A[4]/AA/B/@Caption: Test 4
/Test/Testapp/AA/Reason[1]/@Caption: Test 5
/Test/Testapp/AA/Reason[2]/@Caption: Test 6
/Test/Testapp/AA/Reason[3]/@Caption: Test 7
Примечание: : это единственное решение, представленное на данный момент, которое генерирует точное выражение XPath для любого отдельного атрибута Caption
.
/Test/Testapp/TestappA/A/@Caption
выбирает 4 узла атрибута, тогда как :
/Test/Testapp/TestappA/A[2]/@Caption
выбирает только один атрибутный узел, и это то, что действительно нужно .