A have following xmls:
data_0.xml
data_1.xml
data_3.xml
and so on...
And in xslt file I want to iterate through all files, so I tried for-each function.
<xsl:for-each select="document('data.xml')/*">
How to iterate on all of them? Add mask somehow? This surely won't work:
<xsl:for-each select="document('data_*.xml')/*">
Here is your solution in xslt 1.0:
I have four files in my filesystem:
Doc1.xml:
<p>Doc1</p>
Doc2.xml:
<p>Doc2</p>
Doc3.xml:
<p>Doc3</p>
Doc4.xml:
<p>Doc4</p>
and my xslt to get their output is:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<Root>
<xsl:call-template name="getDocuments"/>
</Root>
</xsl:template>
<xsl:template name="getDocuments">
<xsl:param name="fileStartWith" select="'Doc'"/>
<xsl:param name="endCounter">4</xsl:param>
<xsl:param name="startCounter">1</xsl:param>
<xsl:choose>
<xsl:when test="$endCounter > 0">
<xsl:variable name="fileName"><xsl:value-of select="concat($fileStartWith,$startCounter,'.xml')"/></xsl:variable>
<xsl:for-each select="document($fileName)/*">
<xsl:copy-of select="."/><xsl:text>
</xsl:text>
</xsl:for-each>
<xsl:call-template name="getDocuments">
<xsl:with-param name="startCounter" select="$startCounter + 1"/>
<xsl:with-param name="fileStartWith" select="$fileStartWith"/>
<xsl:with-param name="endCounter" select="$endCounter - 1"/>
</xsl:call-template>
</xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
generated output is:
<Root>
<p>Doc1</p>
<p>Doc2</p>
<p>Doc3</p>
<p>Doc4</p>
</Root>
Please make sure that xslt and xml is on the same path otherwise you need to change the content of document function.
Related
I'm generating a CSV file from an XML using XSL. The XML contains Main elements with child elements Tags, which in turn contain varying amounts of child elements Tag. A part of the XML looks for example like this:
<Main>
<Tags>
<Tag>tag1</Tag>
<Tag>tag2</Tag>
<Tag>tag3</Tag>
</Tags>
</Main>
<Main>
<Tags>
<Tag>tag1</Tag>
<Tag>tag2</Tag>
<Tag>tag3</Tag>
<Tag>tag4</Tag>
<Tag>tag5</Tag>
<Tag>tag6</Tag>
</Tags>
</Main>
In the XSL I have a for each loop that goes through all my Main elements of my XML file. I want to print the values for all the Tag elements. I do this in another for-each loop which is inside the major loop. However, I always want to iterate 10 times, regardless of the amount of Tag elements. I want to print some text in each of the remaining iterations when I have exceeded the amount of printable Tag.
This is the output I'm after:
tag1,tag2,tag3,1,1,1,1,1,1,1,
tag1,tag2,tag3,tag4,tag5,tag6,1,1,1,1,
After the Tag for each loop, I'm calling a template, providing a variable with the amount of Tag in Tags. I then want the template to call itself recursively until it has done the varying amount of remaining iterations for the Tag elements of the current Main element. The amount of Tag elements changes with each Main iteration, which I suspect is a problem in my current solution (which causes my transformation software, Notepad++ with XML Tools, to crash):
<xsl:template match="/">
<xsl:for-each select="Main">
<xsl:for-each select="Tags/Tag">
<xsl:value-of select="Tag"/>
<xsl:text>,</xsl:text>
</xsl:for-each>
<xsl:call-template name="repeatable">
<xsl:with-param name="tagamount" select="count(Tags/*)"/>
</xsl:call-template>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
<xsl:template name="repeatable">
<xsl:param name="tagamount"/>
<xsl:param name="index" select="0" />
<xsl:text>1,</xsl:text>
<xsl:if test="not($index = 10-$tagamount)">
<xsl:call-template name="repeatable">
<xsl:with-param name="index" select="$index + 1" />
</xsl:call-template>
</xsl:if>
</xsl:template>
Does anyone have any idea if it's possible to do this type of varying iteration, or am I out of luck?
Edit:
I managed to solve it. The problem was I had forgotten to pass on the variable tagamount with each recursive call. See my solution further below.
I couldn't wrap my head around your code. How about something simpler?
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:variable name="sep" select="','"/>
<xsl:variable name="LF" select="'
'"/>
<xsl:variable name="filler" select="'1,2,3,4,5,6,7,8,9,10'"/>
<xsl:template match="/">
<xsl:for-each select="rt/Main/Tags">
<xsl:for-each select="Tag">
<xsl:value-of select="concat(., $sep)"/>
</xsl:for-each>
<xsl:value-of select="substring($filler, 2*count(Tag)+1)"/>
<xsl:value-of select="$LF"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Note:
1. Your XML is missing a root element: I am using "rt" as a placeholder.
2. For testing purposes, I have changed "1,1,1,..." into "1,2.3...".
Here is one way to do it.
This XSLT stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<!-- Sets the number of iterations per Tags element. -->
<xsl:variable name="maximum" select="10"/>
<!-- Matches all the Tags elements and calls a recursive template, intializing the count to 1. -->
<xsl:template match="//Tags">
<xsl:call-template name="output-tags">
<xsl:with-param name="count" select="1"/>
</xsl:call-template>
<xsl:text>
</xsl:text>
</xsl:template>
<!-- A recursive template that will repeat itself until its count reaches the maximum value.
If the count is equal to or less then the number of Tag elements inside the current Tags
element, then find the Tag element in the count position and print its value. Otherwise,
print 1. -->
<xsl:template name="output-tags">
<xsl:param name="count"/>
<xsl:if test="$count <= $maximum">
<xsl:choose>
<xsl:when test="$count <= count(Tag)">
<xsl:value-of select="Tag[count(preceding-sibling::Tag) = $count - 1]"/>
<xsl:text>,</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>1,</xsl:text>
</xsl:otherwise>
</xsl:choose>
<xsl:call-template name="output-tags">
<xsl:with-param name="count" select="$count + 1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
produces the following output when applied to your example input XML:
tag1,tag2,tag3,1,1,1,1,1,1,1,
tag1,tag2,tag3,tag4,tag5,tag6,1,1,1,1,
Thanks for the answers!
I managed to solve it right after I posted. The problem was I had forgotten to send the tagamount variable with the recursive call. After adding it, it works. The repeatable template then looks like this:
<xsl:param name="tagamount"/>
<xsl:param name="index" select="0" />
<xsl:text>1,</xsl:text>
<xsl:if test="not($index = 10-$tagamount)">
<xsl:call-template name="repeatable">
<xsl:with-param name="tagamount" select="$tagamount"/> <-----------
<xsl:with-param name="index" select="$index + 1" />
</xsl:call-template>
</xsl:if>
I have this Entry Input XML
<BusinessInteractionTypes>
<BusinessInteractionType>
<TypeId>123</TypeId>
<Name>Foo</Name>
<Description>XSLT</Description>
</BusinessInteractionType>
...a lot BusinessInteractionType Elements
</BusinessInteractionTypes>
But a have a lot of transformation (to SOA case), I need Transform this input using XSLT to:
<businessInteractionTypes>
<businessInteractionType>
<BusinessInteractionType> <!-- Just first input node -->
<TypeId/>
</BusinessInteractionType>
</businessInteractionType>
<businessInteractionTypeHas> <!-- Recursive interaction began -->
<businessInteractionType>
<BusinessInteractionType>
<TypeId />
</BusinessInteractionType>
<businessInteractionTypeHas>
... recursive
</businessInteractionType>
</businessInteractionTypeHas>
</businessInteractionTypes>
Srs, I fix the issue... follow
Thanks all
<?xml version="1.0" encoding="UTF-8"?>
<xsl:template match="cus:BusinessInteractionType">
<xsl:call-template name="Master">
<xsl:with-param name="nodeD" select="bus1:BusinessInteractionType"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="Master">
<xsl:param name="nodeD" select="bus1:BusinessInteractionType"/>
<cas:businessInteractionTypes>
<cas:businessInteractionType>
<xsl:if test="bus1:BusinessInteractionType[position() = 1]">
<bus3:BusinessInteractionType>
<bus1:TypeId>
<xsl:value-of select="//bus1:TypeId"/>
</bus1:TypeId>
</bus3:BusinessInteractionType>
</xsl:if>
<xsl:call-template name="MasterNested">
<xsl:with-param name="current_node" select="bus1:BusinessInteractionType[position()>1]"/>
</xsl:call-template>
</cas:businessInteractionType>
</cas:businessInteractionTypes>
</xsl:template>
<xsl:template name="MasterNested">
<xsl:param name="current_node" select="bus1:BusinessInteractionType"/>
<xsl:if test="$current_node">
<cas:businessInteractionTypeHas>
<cas:businessInteractionType>
<bus3:TypeId>
<xsl:value-of select="$current_node//bus1:TypeId"/>
</bus3:TypeId>
<bus3:Name>
<xsl:value-of select="$current_node//bus1:Name"/>
</bus3:Name>
<bus3:Description>
<xsl:value-of select="$current_node//bus1:Description"/>
</bus3:Description>
</cas:businessInteractionType>
<xsl:call-template name="MasterNested">
<xsl:with-param name="current_node" select="$current_node/following-sibling::node()"/>
</xsl:call-template>
</cas:businessInteractionTypeHas>
</xsl:if>
</xsl:template>
How to split an elements using ; as delimiter.my requirement is like below.
input:
<Element1>C:KEK39519US; U:085896395195; A:K39519US; B:S2345843</Element1>
output:
<CustItem>KEK39519US</CustItem>
<UNumber>085896395195</UNumber>
<ANumber>K39519US</ANumber>
<BNumber>S2345843</BNumber>
the input is every time not same.some times it comes like C:KEK39519US; U:085896395195; B:S2345843
some time like this C:KEK39519US; A:K39519US; B:S2345843
sometime like this U:085896395195; A:K39519US;
sometime like this C:KEK39519US; U:085896395195; A:K39519US;
To solve this in XSLT 1.0 you may need a named template which recursively calls itself. The template will process of the string before the first semi-colon, and output the element accordingly. It will then recursively call itself with the remaining part of the string after this semi-colon (if there is one)
Here is the full XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="Element1">
<xsl:call-template name="outputElements">
<xsl:with-param name="list" select="." />
</xsl:call-template>
</xsl:template>
<xsl:template name="outputElements">
<xsl:param name="list"/>
<xsl:variable name="first" select="normalize-space(substring-before(concat($list, ';'), ';'))"/>
<xsl:variable name="remaining" select="normalize-space(substring-after($list, ';'))"/>
<xsl:call-template name="createElement">
<xsl:with-param name="element" select="$first" />
</xsl:call-template>
<!-- If there are still elements left in the list, call the template recursively -->
<xsl:if test="$remaining">
<xsl:call-template name="outputElements">
<xsl:with-param name="list" select="$remaining"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="createElement">
<xsl:param name="element"/>
<xsl:variable name="elementName">
<xsl:choose>
<xsl:when test="substring-before($element, ':') = 'C'">CustItem</xsl:when>
<xsl:otherwise><xsl:value-of select="concat(substring-before($element, ':'), 'Number')" /></xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:element name="{$elementName}">
<xsl:value-of select="substring-after($element, ':')" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
When applied to you XML, the following is output
<CustItem>KEK39519US</CustItem>
<UNumber>085896395195</UNumber>
<ANumber>K39519US</ANumber>
<BNumber>S2345843</BNumber>
Note the use of Attribute Value Templates in specifying the name of each new element.
I'm trying to think functional, in XSLT terms, as much as possible, but in this case, I really don't see how to do it without tweaking. I have roughly this data structure:
<transactions>
<trx>
<text>abc</text>
<text>def</text>
<detail>
<text>xxx</text>
<text>yyy</text>
<text>zzz</text>
</detail>
</trx>
</transactions>
Which I roughly want to flatten into this form
<row>abc</row>
<row>def</row>
<row>xxx</row>
<row>yyy</row>
<row>zzz</row>
But the tricky thing is: I want to create chunks of 40 text-rows and transactions mustn't be split across chunks. I.e. if my current chunk already has 38 rows, the above transaction would have to go into the next chunk. The current chunk would need to be filled with two empty rows to complete the 40:
<row/>
<row/>
In imperative/procedural programming, it's very easy. Just create a global iterator variable counting to multiples of 40, and insert empty rows if needed (I have provided an answer showing how to tweak XSLT/Xalan to allow for such variables). But how to do it with XSLT? N.B: I'm afraid recursion is not possible considering the size of data I'm processing... But maybe I'm wrong on that
I. Here is an XSLT 1.0 solution (the XSLT 2.0 solution is much easier):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="pChunkSize" select="8"/>
<xsl:param name="vChunkSize" select="$pChunkSize+1"/>
<xsl:variable name="vSheet" select="document('')"/>
<xsl:variable name="vrtfEmptyChunk">
<xsl:for-each select=
"($vSheet//node())[not(position() > $pChunkSize)]">
<row/>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="vEmptyChunk" select=
"ext:node-set($vrtfEmptyChunk)/*"/>
<xsl:variable name="vrtfDummy">
<delete/>
</xsl:variable>
<xsl:variable name="vDummy" select="ext:node-set($vrtfDummy)/*"/>
<xsl:template match="/*">
<chunks>
<xsl:call-template name="fillChunks">
<xsl:with-param name="pNodes" select="trx"/>
<xsl:with-param name="pCurChunk" select="$vDummy"/>
</xsl:call-template>
</chunks>
</xsl:template>
<xsl:template name="fillChunks">
<xsl:param name="pNodes"/>
<xsl:param name="pCurChunk"/>
<xsl:choose>
<xsl:when test="not($pNodes)">
<chunk>
<xsl:apply-templates mode="rename" select="$pCurChunk[self::text]"/>
<xsl:copy-of select=
"$vEmptyChunk[not(position() > $vChunkSize - count($pCurChunk))]"/>
</chunk>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="vAvailable" select=
"$vChunkSize - count($pCurChunk)"/>
<xsl:variable name="vcurNode" select="$pNodes[1]"/>
<xsl:variable name="vTrans" select="$vcurNode//text"/>
<xsl:variable name="vNumNewNodes" select="count($vTrans)"/>
<xsl:choose>
<xsl:when test="not($vNumNewNodes > $vAvailable)">
<xsl:variable name="vNewChunk"
select="$pCurChunk | $vTrans"/>
<xsl:call-template name="fillChunks">
<xsl:with-param name="pNodes" select="$pNodes[position() > 1]"/>
<xsl:with-param name="pCurChunk" select="$vNewChunk"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<chunk>
<xsl:apply-templates mode="rename" select="$pCurChunk[self::text]"/>
<xsl:copy-of select=
"$vEmptyChunk[not(position() > $vAvailable)]"/>
</chunk>
<xsl:call-template name="fillChunks">
<xsl:with-param name="pNodes" select="$pNodes"/>
<xsl:with-param name="pCurChunk" select="$vDummy"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="text" mode="rename">
<row>
<xsl:value-of select="."/>
</row>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the following XML document (based on the provided one, but with three trxelements):
<transactions>
<trx>
<text>abc</text>
<text>def</text>
<detail>
<text>xxx</text>
<text>yyy</text>
<text>zzz</text>
</detail>
</trx>
<trx>
<text>abc2</text>
<text>def2</text>
</trx>
<trx>
<text>abc3</text>
<text>def3</text>
<detail>
<text>xxx3</text>
<text>yyy3</text>
<text>zzz3</text>
</detail>
</trx>
</transactions>
the wanted, correct result (two chunks with size 8) is produced:
<chunks>
<chunk>
<row>abc</row>
<row>def</row>
<row>xxx</row>
<row>yyy</row>
<row>zzz</row>
<row>abc2</row>
<row>def2</row>
<row/>
</chunk>
<chunk>
<row>abc3</row>
<row>def3</row>
<row>xxx3</row>
<row>yyy3</row>
<row>zzz3</row>
<row/>
<row/>
<row/>
</chunk>
</chunks>
Do note:
The first two transactions' text elements total number is 7 and they fit in one 8-place chunk.
The third transaction has 5 text elements and doesn't fit in the remaining space of the first chunk -- it is put in a new chunk.
II. XSLT 2.0 Solution (using FXSL)
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:f="http://fxsl.sf.net/"
xmlns:dvc-foldl-func="dvc-foldl-func"
exclude-result-prefixes="f dvc-foldl-func"
>
<xsl:import href="../f/func-dvc-foldl.xsl"/>
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="pChunkSize" select="8"/>
<dvc-foldl-func:dvc-foldl-func/>
<xsl:variable name="vPadding">
<row/>
</xsl:variable>
<xsl:variable name="vFoldlFun" select="document('')/*/dvc-foldl-func:*[1]"/>
<xsl:template match="/">
<xsl:variable name="vpaddingChunk" select=
"for $i in 1 to $pChunkSize
return ' '
"/>
<xsl:variable name="vfoldlResult" select=
"f:foldl($vFoldlFun, (), /*/trx),
$vpaddingChunk
"/>
<xsl:variable name="vresultCount"
select="count($vfoldlResult)"/>
<xsl:variable name="vFinalResult"
select="subsequence($vfoldlResult, 1,
$vresultCount - $vresultCount mod $pChunkSize
)"/>
<result>
<xsl:for-each select="$vFinalResult">
<row>
<xsl:value-of select="."/>
</row>
</xsl:for-each>
<xsl:text>
</xsl:text>
</result>
</xsl:template>
<xsl:template match="dvc-foldl-func:*" mode="f:FXSL">
<xsl:param name="arg1"/>
<xsl:param name="arg2"/>
<xsl:variable name="vCurCount" select="count($arg1)"/>
<xsl:variable name="vNewCount" select="count($arg2//text)"/>
<xsl:variable name="vAvailable" select=
"$pChunkSize - $vCurCount mod $pChunkSize"/>
<xsl:choose>
<xsl:when test="$vNewCount le $vAvailable">
<xsl:sequence select="$arg1, $arg2//text"/>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="$arg1"/>
<xsl:for-each select="1 to $vAvailable">
<xsl:sequence select="$vPadding/*"/>
</xsl:for-each>
<xsl:sequence select="$arg2//text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the same XML document (above), the same correct, wanted result is produced:
<result>
<row>abc</row>
<row>def</row>
<row>xxx</row>
<row>yyy</row>
<row>zzz</row>
<row>abc2</row>
<row>def2</row>
<row/>
<row>abc3</row>
<row>def3</row>
<row>xxx3</row>
<row>yyy3</row>
<row>zzz3</row>
<row> </row>
<row> </row>
<row> </row>
</result>
Do note:
The use of the f:foldl() function.
A special DVC (Divide and Conquer) variant of f:foldl() so that recursion stack overflow is avoided for all practical purposes -- for example, the maximum recursion stack depth for 1000000 (1M) trx elements is just 19.
Build the complete XML data structure as you need in Java. Then, do the simple iteration in XSL over prepared XML.
You might save a lot of effort and provide a maintainable solution.
As promised a simplified example answer showing how Xalan can be tweaked to allow for incrementing such global iterators:
<xsl:stylesheet version="1.0" xmlns:f="xalan://com.example.Functions">
<!-- the global row counter variable -->
<xsl:variable name="row" select="0"/>
<xsl:template match="trx">
<!-- wherever needed, the $row variable can be globally incremented -->
<xsl:variable name="iteration" value="f:increment('row')"/>
<!-- based upon this variable, calculations can be made -->
<xsl:variable name="remaining-rows-in-chunk"
value="40 - (($iteration - 1) mod 40) "/>
<xsl:if test="count(.//text) > $remaining-rows-in-chunk">
<xsl:call-template name="empty-row">
<xsl:with-param name="rows" select="$remaining-rows-in-chunk"/>
</xsl:call-template>
</xsl:if>
<!-- process transaction now, that previous chunk has been filled [...] -->
</xsl:template>
<xsl:template name="empty-row">
<xsl:param name="rows"/>
<xsl:if test="$rows > 0">
<row/>
<xsl:variable name="dummy" select="f:increment('row')"/>
<xsl:call-template name="empty-row">
<xsl:with-param name="rows" select="$rows - 1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
And the contents of com.example.Functions:
public class Functions {
public static String increment(ExpressionContext context, String nodeName) {
XNumber n = null;
try {
// Access the $row variable
n = ((XNumber) context.getVariableOrParam(new QName(nodeName)));
// Make it "mutable" using this tweak. I feel horrible about
// doing this, though ;-)
Field m_val = XNumber.class.getDeclaredField("m_val");
m_val.setAccessible(true);
// Increment it
m_val.setDouble(n, m_val.getDouble(n) + 1.0);
} catch (Exception e) {
log.error("Error", e);
}
return n == null ? null : n.str();
}
}
I'm trying to write a template that will grab a mixture of text nodes and elements within a parent element and create a new node. I've done a lot of searching and couldn't find what I was looking for...so hopefully I'm not asking to basic a question.
Here is a sample of xml I want to transform:
<?xml version="1.0"?>
<root>
<para>Here is some text that will ask users to enter a <rule-line/> [<emph type="it">date</emph>], and maybe their <rule-line/> [<emph type="it">name</emph>]. The text could come in different [<emph type="it">order</emph>] <rule-line/>, and their could be any number of instances.</para>
</root>
I want to group the bracketed text and the rule into a new element like so:
<entry>[<emph type"it">date</emph>]</entry>
I have a template that can identify the text I want to change, and I can change it, but I don't know how to add the text I want to the result tree and omit the old text.
Here are the relevant templates:
<xsl:template match="para">
<xsl:for-each select="* | text()">
<xsl:choose>
<xsl:when test="self::rule-line and following-sibling::node()[1][starts-with(., ' [')] and string(node-name(following-sibling::node()[2])) = 'emph' and following-sibling::node()[3][starts-with(., ']')]">
<xsl:comment>made match</xsl:comment>
<xsl:call-template name="codeEntry">
<xsl:with-param name="rule" select="."/>
<xsl:with-param name="openBracket" select="following-sibling::node()[1]"/>
<xsl:with-param name="emphTag" select="following-sibling::node()[2]"/>
<xsl:with-param name="closeBracketString" select="following-sibling::node()[3]"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
<xsl:template name="codeEntry">
<xsl:param name="rule"/>
<xsl:param name="openBracket"/>
<xsl:param name="emphTag"/>
<xsl:param name="closeBracketString"/>
<entry>
<xsl:copy-of select="$openBracket"/>
<xsl:copy-of select="$emphTag"/>
<xsl:text>] </xsl:text>
</entry>
<xsl:value-of select="substring-after($closeBracketString, ']')"/>
</xsl:template>
Obviously, the when statement grabs a group of nodes, but when each node goes through the otherwise block it gets copied to the result tree. I'm not really sure how to handle this since the para could have any number of these node groupings in any order, or none. (Once I figure this out I'll add another when block that deals with the bracketed text before the rule)
I think creating a variable that tells the template to ignore the node is the way to go...but I'm a little foggy on the immutable variables and their scope...
I was also trying to think of a way I could try to do this recursively...but that would require adding a start tag at one point, an end tag in another, or no tag if the node being processed is in the middle of the sequence...and I know that can get weird in xslt.
Anyone run into this type of situation before?
thanks,
jason
any ideas
Just for fun (What a mess of a schema!), this stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="node()|#*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="rule-line"/>
<xsl:template match="emph">
<entry>
<xsl:text>[</xsl:text>
<xsl:call-template name="identity"/>
<xsl:text>]</xsl:text>
</entry>
</xsl:template>
<xsl:template match="text()[normalize-space()='[']
[following-sibling::*[1][self::emph]] |
text()[normalize-space()=']']
[preceding-sibling::*[1][self::emph]]"
priority="1"/>
<xsl:template match="text()[starts-with(normalize-space(),']')]
[preceding-sibling::*[1][self::emph]]">
<xsl:value-of select="substring-after(.,']')"/>
</xsl:template>
<xsl:template match="text()[substring(normalize-space(),
string-length(normalize-space()),
1) = '[']
[following-sibling::*[1][self::emph]]">
<xsl:call-template name="crop-both">
<xsl:with-param name="pString" select="concat(']',.)"/>
</xsl:call-template>
</xsl:template>
<xsl:template match="text()[starts-with(normalize-space(),']')]
[substring(normalize-space(),
string-length(normalize-space()),
1) = '[']
[preceding-sibling::*[1][self::emph]]
[following-sibling::*[1][self::emph]]"
priority="1" name="crop-both">
<xsl:param name="pString" select="."/>
<xsl:variable name="vReverse">
<xsl:call-template name="reverse">
<xsl:with-param name="pString"
select="substring-after(.,']')"/>
</xsl:call-template>
</xsl:variable>
<xsl:call-template name="reverse">
<xsl:with-param name="pString"
select="substring-after($vReverse,'[')"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="reverse">
<xsl:param name="pString"/>
<xsl:if test="$pString!=''">
<xsl:call-template name="reverse">
<xsl:with-param name="pString"
select="substring($pString,2)"/>
</xsl:call-template>
<xsl:value-of select="substring($pString,1,1)"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Output:
<root>
<para>Here is some text that will ask users to enter a <entry>[<emph type="it">date</emph>]</entry>, and maybe their <entry>[<emph type="it">name</emph>]</entry>. The text could come in different <entry>[<emph type="it">order</emph>]</entry>, and their could be any number of instances.</para>
</root>