问题
I have an XML of this kind:
<xml>
<node1>1.1</node1>
<node1>1.2</node1>
<node1>1.3</node1>
<node2>2.1</node2>
<node2>2.2</node2>
<node2>2.3</node2>
<node3>3.1</node3>
<node3>3.2</node3>
<node3>3.3</node3>
</xml>
and I want to get the following output:
line: 1.1 + 2.1 + 3.1
line: 1.2 + 2.2 + 3.2
line: 1.3 + 2.3 + 3.3
Is there a way I can iterate over these nodes simultaneously and keep track of my current position in each of three lists or do I have to wrap these items into a bigger block and iterate over blocks?
I'm using XSL 1.0.
回答1:
As easy as this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node1">
<xsl:variable name="vPos" select="position()"/>
<xsl:value-of select=". + ../node2[$vPos] + ../node3[$vPos]"/>
<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
When this transformation is applied on the following XML document (based on the provided one):
<xml>
<node1>1</node1>
<node1>2</node1>
<node1>3</node1>
<node2>4</node2>
<node2>5</node2>
<node2>6</node2>
<node3>7</node3>
<node3>8</node3>
<node3>9</node3>
</xml>
the wanted, correct result is produced:
12
15
18
Do note:
In XSLT 1.0 and XSLT 2.0 one can use the FXSL template/function zip-with3().
In XPath 3.0 (XSLT 3.0) there will be a standard function map-pairs(), but there is no
map-tripples()
standard function. One can use this function to produce an intermediate result and then use it again to produce the final result.As noted by Ian Roberts, the presence of
xsl:strip-space
in this solution is important -- without it theposition()
function produces different results and the transformation doesn't perform as required.
回答2:
I don't think there is a builtin way in XSLT to do that. I guess you could implement it yourself easily by applying the MapReduce technique.
回答3:
I think the following does the job (independent of the number and names of child elements of the xml
element):
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:key name="name" match="xml/*" use="local-name()"/>
<xsl:variable name="first" select="xml/*[generate-id() = generate-id(key('name', local-name())[1])]"/>
<xsl:template match="xml">
<xsl:apply-templates select="$first[1]" mode="init"/>
</xsl:template>
<xsl:template match="xml/*" mode="init">
<xsl:apply-templates select="key('name', local-name())" mode="line"/>
</xsl:template>
<xsl:template match="xml/*" mode="line">
<xsl:variable name="pos" select="position()"/>
<xsl:text>line: </xsl:text>
<xsl:for-each select="$first">
<xsl:if test="position() > 1"><xsl:text> + </xsl:text></xsl:if>
<xsl:apply-templates select="key('name', local-name())[$pos]"/>
</xsl:for-each>
<xsl:text> </xsl:text>
</xsl:template>
<xsl:template match="xml/*">
<xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>
来源:https://stackoverflow.com/questions/13379853/parallel-iteration-over-xml-nodes-with-xslt