Add child elements in particular order - xslt

I am working on an xml to xml transform via XSLT. I Have the following:
stylesheet.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.test.org"
xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0"> <!-- xs namespace allows typed functions and parameters -->
<xsl:output method="xml" indent="yes" version="1.0" encoding="UTF-8" omit-xml-declaration="yes"/>
<xsl:strip-space elements="*" />
<xsl:param name="other-id" select="Request/Order/OtherId" />
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:copy-of select="#*" copy-namespaces="no" />
<xsl:apply-templates select="#*|node()" />
</xsl:element>
</xsl:template>
<xsl:template match="#*|text()|comment()|processing-instruction()">
<xsl:copy />
</xsl:template>
<xsl:template match="/*">
<xsl:apply-templates select="node()" />
</xsl:template>
<xsl:template match="Details">
<xsl:element name="{local-name()}">
<xsl:element name="Signon>
<xsl:element name="SignonDt>2017-01-01</xsl:element>
<xsl:element name="MessageQuantity">3</xsl:element>
<xsl:element>
<xsl:apply-templates />
</xsl:element>
</xsl:template>
<xsl:template match="NotificationRq">
<xsl:element name="{local-name()}">
<xsl:element name="RqUID">Test</xsl:element>
<xsl:apply-templates />
</xsl:element>
</xsl:template>
<xsl:template match="Policy/SDDCd" />
<xsl:template match="Policy">
<xsl:element name="{local-name()}">
<xsl:element name="RFDCd">
<xsl:call-template name="getRFDCd" />
</xsl:element>
<xsl:apply-templates />
</xsl:element>
</xsl:template>
<xsl:template name="getRFDCd">
<xsl:choose>
<xsl:when test="contains($other-id, 'RFD 2')">
<xsl:text>AUB</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>CL</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="Contact/Addr" >
<xsl:element name="{local-name()}">
<xsl:element name="AddrTypeCd">StreetAddress</xsl:element>
<xsl:apply-templates />
</xsl:element>
</xsl:template>
<!-- other templates structured similar to these -->
input.xml
<Request>
<Details>
<NotificationRq>
<RqDate>2017-01-01</RqDate>
<RqDetails>
<!-- other children -->
<RqDetails>
</NotificationRq>
<Policy>
<PolNumber>1234567890</PolNumber>
<SDDCd>T35</SDDCd>
</Policy>
<Contact>
<Addr>
<AddrLn1>Test address line 1</AddrLn1>
<AddrLn2>Test address line 2</AddrLn2>
<PostCode>AX12D3</PostCode>
</Addr>
</Contact>
<!-- other children -->
</Details>
</Request>
output.xml
<Details xmlns="http://test.org">
<Signon>
<SignonDt>2017-01-01</Signon>
<MessageQuantity>3</MessageQuantity>
</Signon>
<NotificationRq>
<RqUId>Test</RqUID>
<RqDate>2017-01-01</RqDate>
<RqDetails>
<!-- other children -->
<RqDetails>
</NotificationRq>
<Policy>
<RFDCd>CL</RFDCd>
<PolNumber>1234567890</PolNumber>
</Policy>
<Contact>
<Addr>
<AddrTypeCd>StreetAddress</AddrTypeCd>
<AddrLn1>Test address line 1</AddrLn1>
<AddrLn2>Test address line 2</AddrLn2>
<PostCode>AX12D3</PostCode>
</Addr>
</Contact>
<!-- other children -->
</Details>
Note: I am using <xsl:element name="{local-name()}"> due to an earlier issue; i am copying source xml which has no namespace to new output which does, and this solution, along with the extra template alongside the identity transform fixed the issue of a namespace attribute being added to all elements
The problem is, this xml is sent downstream where schema validation occurs, and because of this, position of newly created child elements matter. As shown in the snippets above, new child elements are added as the first child of the parent. For some cases this is fine and this is where they should be, but for around half of the created elements, they must appear last(with the odd one or two requiring insertion at a specified position). Still using input.xml as an example, here is how the desired output should look:
desired-output.xml
<Details xmlns="http://test.org">
<NotificationRq>
<RqDate>2017-01-01</RqDate>
<RqDetails>
<!-- other children -->
<RqDetails>
<RqUId>Test</RqUID>
</NotificationRq>
<Signon>
<SignonDt>2017-01-01</Signon>
<MessageQuantity>3</MessageQuantity>
</Signon>
<Policy>
<PolNumber>1234567890</PolNumber>
<RFDCd>CL</RFDCd>
</Policy>
<Contact>
<Addr>
<AddrLn1>Test address line 1</AddrLn1>
<AddrLn2>Test address line 2</AddrLn2>
<AddrTypeCd>StreetAddress</AddrTypeCd>
<PostCode>AX12D3</PostCode>
</Addr>
</Contact>
<!-- other children -->
</Details>
Is there a way to specify where in the existing child order of an element the new child should appear? Is one of my templates causing the insertion order to always be in the first position?
Additional Info: I have seen a few questions on specific order insertion, but they usually seem to be for a sequence of elements, or a series of repeating elements, e.g. how do I insert another author element in a series of author elements, and the solutions tend to make use of a position function to determine if one has looped to the right index, and then insert. The xml I am working with is made up of unique elements which may hold a value or may contain several children(with some of those containing children etc). There are no repeating elements in the xml, so I don't think I can make use of a solution as described above(unless someone knows how to do such a thing for non repeating children of an element). Also, I am using Saxon HE version 9.7.0-8

Why can't you do simply:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.test.org">
<xsl:output method="xml" indent="yes" version="1.0" encoding="UTF-8" omit-xml-declaration="yes"/>
<xsl:strip-space elements="*" />
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:copy-of select="#*" />
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="comment()|processing-instruction()">
<xsl:copy/>
</xsl:template>
<xsl:template match="/*">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="NotificationRq">
<NotificationRq>
<xsl:apply-templates/>
<RqUId>Test</RqUId>
</NotificationRq>
</xsl:template>
</xsl:stylesheet>

Related

XSLT – creating a network from all children elements

With XSLT 2.0, I am trying to create a list of relations between all children of given elements, in a document such as:
<doc>
<part1>
<name>John</name>
<name>Paul</name>
<name>George</name>
<name>Ringo</name>
<place>Liverpool</place>
</part1>
<part2>
<name>Romeo</name>
<name>Romeo</name>
<name>Juliet</name>
<fam>Montague</fam>
<fam>Capulet</fam>
</part2>
</doc>
The result I would like to obtain, ideally by conflating and weighing the identical relations, would be (in whatever order) something like:
<doc>
<part1>
<rel><name>John</name><name>Paul</name></rel>
<rel><name>John</name><name>George</name></rel>
<rel><name>John</name><name>Ringo</name></rel>
<rel><name>Paul</name><name>George</name></rel>
<rel><name>Paul</name><name>Ringo</name></rel>
<rel><name>George</name><name>Ringo</name></rel>
<rel><name>John</name><place>Liverpool</place></rel>
<rel><name>Paul</name><place>Liverpool</place></rel>
<rel><name>George</name><place>Liverpool</place></rel>
<rel><name>Ringo</name><place>Liverpool</place></rel>
</part1>
<part2>
<rel weight="2"><name>Romeo</name><name>Juliet</name></rel>
<rel weight="2"><name>Romeo</name><fam>Montague</fam></rel>
<rel weight="2"><name>Romeo</name><fam>Capulet</fam></rel>
<rel><name>Juliet</name><fam>Montague</fam></rel>
<rel><name>Juliet</name><fam>Capulet</fam></rel>
<rel><fam>Montague</fam><fam>Capulet</fam></rel>
</part2>
</doc>
—but I'm not sure how to proceed. Many thanks in advance for your help.
You still haven't explained the logic that needs to be applied here, so this is based largely on a guess:
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="/">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="doc/*">
<!-- first pass-->
<xsl:variable name="unique-items">
<xsl:for-each-group select="*" group-by="concat(name(), '|', .)">
<item name="{name()}" count="{count(current-group())}" value="{.}"/>
</xsl:for-each-group>
</xsl:variable>
<!-- output -->
<xsl:copy>
<xsl:for-each select="$unique-items/item">
<xsl:variable name="left" select="."/>
<xsl:for-each select="following-sibling::item">
<xsl:variable name="weight" select="$left/#count * #count" />
<rel>
<xsl:if test="$weight gt 1">
<xsl:attribute name="weight" select="$weight"/>
</xsl:if>
<xsl:apply-templates select="$left | ." />
</rel>
</xsl:for-each>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<xsl:template match="item">
<xsl:element name="{#name}">
<xsl:value-of select="#value"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
The idea here is to remove duplicates in the first pass, then enumerate all combinations in the second (final) pass. The weight is computed by multiplying the number of occurrences of each member of a combination pair and shown only when it exceeds 1.
At least the combinatoric part of your problem could be solved with the following XSLT script. It does not solve the elimination of duplicates, but that could possibly be done in a second transformation.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- standard copy template -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="doc/*">
<xsl:copy>
<xsl:variable name="l" select="./*"/>
<xsl:for-each select="$l">
<xsl:variable name="a" select="."/>
<xsl:variable name="posa" select="position()"/>
<xsl:variable name="namea" select="name()"/>
<xsl:for-each select="$l">
<xsl:if test="position() > $posa and (. != $a or name() != $namea)">
<rel>
<xsl:copy-of select="$a"/>
<xsl:copy-of select="."/>
</rel>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When applied to the first part of your example, this produces:
<part1>
<rel><name>John</name><name>Paul</name></rel>
<rel><name>John</name><name>George</name></rel>
<rel><name>John</name><name>Ringo</name></rel>
<rel><name>John</name><place>Liverpool</place></rel>
<rel><name>Paul</name><name>George</name></rel>
<rel><name>Paul</name><name>Ringo</name></rel>
<rel><name>Paul</name><place>Liverpool</place></rel>
<rel><name>George</name><name>Ringo</name></rel>
<rel><name>George</name><place>Liverpool</place></rel>
<rel><name>Ringo</name><place>Liverpool</place></rel>
</part1>
Which seems about correct. If have no idea if the duplicate elimination (or weighting, as you call it) could be done in the same transformation.

Add mandatory nodes with XSLT

I am facing an xslt/xpath problem and hope someone could help, in a few words here is what I try to achieve.
I have to transform an XML document where some nodes may be missing, these missing nodes are mandatory in the final result. I have the set of mandatory node names available in an xsl:param.
The base document is:
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="TRANSFORM.xslt"?>
<BEGIN>
<CLIENT>
<NUMBER>0021732561</NUMBER>
<NAME1>John</NAME1>
<NAME2>Connor</NAME2>
</CLIENT>
<PRODUCTS>
<PRODUCT_ID>12</PRODUCT_ID>
<DESCRIPTION>blah blah</DESCRIPTION>
</PRODUCTS>
<PRODUCTS>
<PRODUCT_ID>13</PRODUCT_ID>
<DESCRIPTION>description ...</DESCRIPTION>
</PRODUCTS>
<OPTIONS>
<OPTION_ID>1</OPTION_ID>
<DESCRIPTION>blah blah blah ...</DESCRIPTION>
</OPTIONS>
<PROMOTIONS>
<PROMOTION_ID>1</PROMOTION_ID>
<DESCRIPTION>blah blah blah ...</DESCRIPTION>
</PROMOTIONS>
</BEGIN>
Here is the stylesheet so far:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:param name="mandatoryNodes" as="xs:string*" select=" 'PRODUCTS', 'OPTIONS', 'PROMOTIONS' "/>
<xsl:template match="/">
<xsl:apply-templates select="child::node()"/>
</xsl:template>
<xsl:template match="node()">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="BEGIN">
<xsl:element name="BEGIN">
<xsl:for-each select="$mandatoryNodes">
<!-- If there is no node with this name -->
<xsl:if test="count(*[name() = 'current()']) = 0">
<xsl:element name="{current()}" />
</xsl:if>
</xsl:for-each>
<xsl:apply-templates select="child::node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
I tried the transformation in XML Spy, the xsl:iftest failed saying that 'current item is PRODUCTS of type xs:string.
I've tried the same xsl:if outside of a for-each and it seems to work ... what am I missing ?
Inside of <xsl:for-each select="$mandatoryNodes"> the context item is a string but you want to access the primary input document and its nodes so you need to store that document or the template's context node in a variable and use that e.g.
<xsl:template match="BEGIN">
<xsl:variable name="this" select="."/>
<xsl:element name="BEGIN">
<xsl:for-each select="$mandatoryNodes">
<!-- If there is no child node of `BEGIN` with this name -->
<xsl:if test="count($this/*[name() = current()]) = 0">
<xsl:element name="{current()}" />
</xsl:if>
</xsl:for-each>
<xsl:apply-templates select="child::node()"/>
</xsl:element>
</xsl:template>

Multi-document xslt, value-of with addition misbehaving

I am trying to write something for another individual and im stuck on the final part of the stylesheet.
We have two XML Documents:
TestXML.xml:
<rootNode>
<header>
<title agg="sum">1</title>
<records agg="sum">10</records>
<number agg="min">5</number>
</header>
</rootNode>
and TestXMLTwo.xml:
<rootNode>
<header>
<title agg="sum">2</title>
<records agg="sum">20</records>
<number agg="min">15</number>
</header>
</rootNode>
Where if the node has its agg attribute equal to 'sum' we combine the values of the two documents nodes. I am doing this using:
<xsl:param name="InputFileOne">[EditedOut]\TestXML.xml</xsl:param>
<xsl:param name="InputFileTwo">[EditedOut]\TestXMLTwo.xml</xsl:param>
<xsl:template match="node()|#*">
<xsl:call-template name="ConcatFiles"/>
</xsl:template>
<xsl:template name="ConcatFiles">
<xsl:variable name="tempStoreDocOne" select ="document($InputFileOne)/rootNode/header" />
<xsl:variable name="tempStoreDocTwo" select ="document($InputFileTwo)/rootNode/header" />
<xsl:element name="rootNode">
<xsl:element name="header">
<xsl:for-each select="$tempStoreDocOne/node()">
<xsl:choose>
<xsl:when test="./#agg = 'sum'">
<xsl:variable name="tempElementDocTwo" select ="$tempStoreDocTwo/."/>
<xsl:element name="{name(.)}">
<xsl:value-of select=". + $tempElementDocTwo"/>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:element name="{name(.)}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:element>
</xsl:element>
</xsl:template>
However on the line <xsl:value-of select=". + $tempElementDocTwo"/> I just get a value of '22016' for the <title> and 22025 for the <records>. Can someone enlighten me as to where I'm going wrong?
Change <xsl:for-each select="$tempStoreDocOne/node()"> to <xsl:for-each select="$tempStoreDocOne/*">, then add a variable storing the position i.e.
<xsl:variable name="pos" select="position()"/>
inside of the for-each, then change
<xsl:variable name="tempElementDocTwo" select ="$tempStoreDocTwo/."/>
to
<xsl:variable name="tempElementDocTwo" select ="$tempStoreDocTwo/*[$pos]"/>
Currently you are accessing the string value of the complete header element in the second document which is the concatenation of its descendant nodes while you want to access the child element with the same position as the one in the first document.
Here is a much simpler and shorter solution (no explicit conditionals):
<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:param name="pDoc2">
<rootNode>
<header>
<title agg="sum">2</title>
<records agg="sum">20</records>
<number agg="min">15</number>
</header>
</rootNode>
</xsl:param>
<xsl:variable name="vDoc2" select="document('')/*/xsl:param"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="header/*[#agg='sum']">
<title>
<xsl:value-of select=
". + $vDoc2/*/header/*[name()=name(current()) and #agg='sum']"/>
</title>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the first XML document (the second is inlined in the transformation just for convenience):
<rootNode>
<header>
<title agg="sum">1</title>
<records agg="sum">10</records>
<number agg="min">5</number>
</header>
</rootNode>
the wanted, correct result is produced:
<rootNode>
<header>
<title>3</title>
<title>30</title>
<number agg="min">5</number>
</header>
</rootNode>

Count Nodes and Duplicate with no values in XSLT

I am super rusty at XSLT and was wondering if someone can give me some pointers.
Edit: Using XSLT 1.0
Original XML:
<gic>
<application>
<agent>
...child nodes
</agent>
<client>
...child nodes
</client>
<bank>
...child nodes
</bank>
</application>
</gic>
I need to transform the given XML INPUT to have 5 client nodes. The input can contain 1-5 client nodes populated. I need to ensure there is always 5 in the output. In this case, one is provide, so I need to insert 4 client nodes with all child nodes. Values for all child nodes need to be empty. Output in XML
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="application" as="node()*">
<xsl:copy>
<xsl:apply-templates select="agent" />
<!-- copy existing cliens -->
<xsl:apply-templates select="client" />
<!-- add new clients -->
<xsl:call-template name="AddClients">
<xsl:with-param name="times" select="10 - count(client)" />
</xsl:call-template>
<!-- copy banks -->
<xsl:apply-templates select="bank" />
</xsl:copy>
</xsl:template>
<xsl:template name="AddClients">
<xsl:param name="times" select="1" />
<xsl:if test="number($times) > 0">
<!-- new element here -->
<xsl:element name="client">
<xsl:attribute name="a1">
<xsl:value-of select="asas" />
</xsl:attribute>
</xsl:element>
<xsl:call-template name="AddClients">
<xsl:with-param name="times" select="$times - 1" />
</xsl:call-template>
</xsl:if>
</xsl:template>
<!-- default -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

xslt generate children based on split and parent node name

is it possible to do the following in xsl. I'm tring to split the contents of an element and create sub-elements based on the split. To make things trickier there are the occasional exception (ie node-4 doesn't get split). I'm wondering if there is a way i can do this without explicit splits hardcoded for each element. Again, not sure if this is possible. thanks for the help!
original XML:
<document>
<node>
<node-1>hello world1</node-1>
<node-2>hello^world2</node-2>
<node-3>hello^world3</node-3>
<node-4>hello^world4</node-4>
</node>
</document>
transformed XML
<document>
<node>
<node-1>hello world1</node-1>
<node-2>
<node2-1>hello</node2-1>
<node2-2>world2</node2-2>
</node-2>
<node-3>
<node3-1>hello</node3-1>
<node3-2>world3</node3-2>
</node-3>
<node-4>hello^world4</node-4>
</node>
</document>
To make things trickier there are the
occasional exception (ie node-4
doesn't get split). I'm wondering if
there is a way i can do this without
explicit splits hardcoded for each
element.
Pattern matching text nodes to tokenize, this more semantic stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()[contains(.,'^')]" name="tokenize">
<xsl:param name="pString" select="concat(.,'^')"/>
<xsl:param name="pCount" select="1"/>
<xsl:if test="$pString">
<xsl:element name="{translate(name(..),'-','')}-{$pCount}">
<xsl:value-of select="substring-before($pString,'^')"/>
</xsl:element>
<xsl:call-template name="tokenize">
<xsl:with-param name="pString"
select="substring-after($pString,'^')"/>
<xsl:with-param name="pCount" select="$pCount + 1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template match="node-4/text()">
<xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>
Output:
<document>
<node>
<node-1>hello world1</node-1>
<node-2>
<node2-1>hello</node2-1>
<node2-2>world2</node2-2>
</node-2>
<node-3>
<node3-1>hello</node3-1>
<node3-2>world3</node3-2>
</node-3>
<node-4>hello^world4</node-4>
</node>
</document>
Note: A classic tokenizer (In fact, this use a normalized string allowing empty items in sequence). Pattern matching and overwriting rules (preserving node-4 text node).
Here's an XSL 1.0 solution. I presume that the inconsistency in node-4 in your sample output was just a typo. Otherwise you'll have to define why node3 was split and node4 wasn't.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<document>
<node>
<xsl:apply-templates select="document/node/*"/>
</node>
</document>
</xsl:template>
<xsl:template match="*">
<xsl:variable name="tag" select="name()"/>
<xsl:choose>
<xsl:when test="contains(text(),'^')">
<xsl:element name="{$tag}">
<xsl:element name="{concat($tag,'-1')}">
<xsl:value-of select="substring-before(text(),'^')"/>
</xsl:element>
<xsl:element name="{concat($tag,'-2')}">
<xsl:value-of select="substring-after(text(),'^')"/>
</xsl:element>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
This works as long as all the nodes you want split are at the same level, under /document/node. If the real document structure is different you will have to tweak the solution to match.
Can you use XSLT 2.0? If so, it sounds like <xsl:analyze-string> is right up your alley. You can split based on a regexp.
If you need further details, ask...
solution i used:
<xsl:output omit-xml-declaration="yes" method="xml" indent="yes"/>
<xsl:preserve-space elements="*"/>
<xsl:template match="node()|#*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()[1]|#*"/>
</xsl:copy>
<xsl:apply-templates select="following-sibling::node()[1]"/>
</xsl:template>
<xsl:template match="node()" mode="copy">
<xsl:call-template name="identity"/>
</xsl:template>
<xsl:template match="node-2 | node-3" name="subFieldCarrotSplitter">
<xsl:variable name="tag" select="name()"/>
<xsl:element name="{$tag}">
<xsl:for-each select="str:split(text(),'^')">
<xsl:element name="{concat($tag,'-',position())}">
<xsl:value-of select="text()"/>
</xsl:element>
</xsl:for-each>
</xsl:element>
<xsl:apply-templates select="following-sibling::node()[1]"/>
</xsl:template>