How can I change the location of node "//seps/ink[x]/c" to the same position in node "inks/ink[x]". I only found solutions using attributes.
Source:
<?xml version="1.0" encoding="UTF-16"?>
<root>
<inks>
<ink>
<A>A1</A>
<B>B1</B>
</ink>
<ink>
<A>A2</A>
<B>B2</B>
</ink>
</inks>
<seps>
<ink>
<C>C1</C>
</ink>
<ink>
<C>C2</C>
</ink>
</seps>
</root>
Target:
<?xml version="1.0" encoding="UTF-16"?>
<root>
<inks>
<ink>
<A>A1</A>
<B>B1</B>
<C>C1</C>
</ink>
<ink>
<A>A2</A>
<B>B2</B>
<C>C2</C>
</ink>
</inks>
</root>
Thanks a lot for helpful solution.
CodePudding user response:
Well, if you process root/inks/ink
with e.g. xsl:apply-templates
(or with the identity transformation but xsl:strip-space elements="*"/>
in place), you can either store <xsl:variable name="pos" select="position()"/>
and use that variable as an index in the other path e.g. /root/seps/ink[$pos]/C
or in general, if you don't want to rely on processing those ink
elements explicitly, in the template matching e.g. <xsl:template match="root/inks/ink">
, you can always compute the position with xsl:number
in a variable e.g. <xsl:variable name="pos"><xsl:number/></xsl:variable>
. Then you can use that variable with e.g. /root/seps/ink[position() = $pos]/C
.
CodePudding user response:
In XSLT 3.0, you could use xsl:merge
, or the fn:for-each-pair
function.
But 1.0 is more limited, and the only way to merge two sequences is along the lines
<xsl:variable name="root" select="."/>
<xsl:for-each select="inks/ink">
<xsl:variable name="p" select="position()"/>
<xsl:copy-of select="*"/>
<xsl:copy-of select="$root/seps/ink[$p]"/>
</xsl:for-each>
CodePudding user response:
This transformation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="inks/ink">
<xsl:variable name="vPos" select="position()"/>
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
<xsl:apply-templates select="/*/seps/ink[$vPos]/C"/>
</xsl:copy>
</xsl:template>
<xsl:template match="seps"/>
</xsl:stylesheet>
When applied on the provided XML document:
<root>
<inks>
<ink>
<A>A1</A>
<B>B1</B>
</ink>
<ink>
<A>A2</A>
<B>B2</B>
</ink>
</inks>
<seps>
<ink>
<C>C1</C>
</ink>
<ink>
<C>C2</C>
</ink>
</seps>
</root>
Produces the wanted, correct result:
<root>
<inks>
<ink>
<A>A1</A>
<B>B1</B>
<C>C1</C>
</ink>
<ink>
<A>A2</A>
<B>B2</B>
<C>C2</C>
</ink>
</inks>
</root>
Explanation:
The identity rule copies every node "as-is" if a higher priority template doesn't match this node.
Subtrees of elements of type
inks/ink
are processed by the code as in the identity rule, but extended with one final application of it to the correspondingsep/ink/C
element. The current position of the matchedinks/ink
(current()
) element, stored in a variable, is used to select the wanted correspondingseps/ink
elementProcessing of the subtree with root the
seps
element is suppressed (effectively "deleting" it from the result of the transformation, by an empty matching template