specify a temporary document instead of initial source document for a template rule - xslt-3.0

The match attribute of <xsl:template> defines which nodes this
template rule applies to. I think, the matched nodes are implicit coming from initial source document.
For example, here is a part of my XSLT template:
<xsl:mode name="unroll" on-no-match="shallow-copy"/>
<xsl:template match="StructFormat[#repeat]" mode="unroll">
...
</xsl:template>
<xsl:variable name="complete-struct">
<xsl:apply-templates mode="unroll"/>
</xsl:variable>
This template processes the intial source ducument, and the result is saved in a variable. How can I let this template rule apply to a temporary document loaded by document() function? I tried like this and it didn't work:
<xsl:template match="/" mode="unroll">
<xsl:apply-templates select="document('a.xml')/*"/>
</xsl:template>
<xsl:template match="#*|node()" mode="unroll">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>

The part with the global variable
<xsl:variable name="complete-struct">
<xsl:apply-templates mode="unroll"/>
</xsl:variable>
builds a variable processing the child nodes of the global context item (https://www.w3.org/TR/xslt-30/#dt-global-context-item)
you can change that to
<xsl:variable name="complete-struct">
<xsl:apply-templates select="doc('a.xml')/node()" mode="unroll"/>
</xsl:variable>
to process nodes from another document or if you run your XSLT processor with its API check where/how you could set that global context item to your particular document if needed/wanted (see http://saxonica.com/html/documentation/javadoc/net/sf/saxon/s9api/Xslt30Transformer.html#setGlobalContextItem-net.sf.saxon.s9api.XdmItem- for Saxon 9.9).
I think your attempt adding
<xsl:template match="#*|node()" mode="unroll">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
is wrong, your initial code has a declaration <xsl:mode name="unroll" on-no-match="shallow-copy"/> which should do fine and if you wanted to spell it out you would need
<xsl:template match="#*|node()" mode="unroll">
<xsl:copy>
<xsl:apply-templates select="#*|node()" mode="#current"/>
</xsl:copy>
</xsl:template>

Related

pattern for generating JSON output

I'm trying to map XML to JSON using XSLT 3.0
my broad plan is to take the input, map it to some elements in memory, and then map that to 'map's and 'array's to by applying templates and then letting the XSLT serialise that as JSON.
Here is my initial effort:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
version="3.0">
<xsl:output method="json" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="dsl" as="element()">
<epg lastBuildDate="10/4/2019 9:46:00 AM">
</epg>
</xsl:variable>
<xsl:variable name="output">
<xsl:apply-templates select="$dsl" mode="interpret"/>
</xsl:variable>
<xsl:sequence select="$output"/>
</xsl:template>
<xsl:template match="epg" mode="interpret">
<xsl:sequence select="map {
'lastBuildDate' : #lastBuildDate
}"/>
</xsl:template>
</xsl:stylesheet>
sadly I get
Cannot add a map to an XDM node tree
in the 'interpret' template.
The error occurs with
<xsl:variable name="output">
<xsl:apply-templates select="$dsl" mode="interpret"/>
</xsl:variable>
as that is an implicit use of
<xsl:variable name="output">
<xsl:document>
<xsl:apply-templates select="$dsl" mode="interpret"/>
</xsl:document>
</xsl:variable>
While maps can contain nodes as values, nodes can't contain maps.
For that simple example it should work to use
<xsl:variable name="output" as="item()*">
<xsl:apply-templates select="$dsl" mode="interpret"/>
</xsl:variable>
or even
<xsl:variable name="output" as="item()">
<xsl:apply-templates select="$dsl" mode="interpret"/>
</xsl:variable>
or
<xsl:variable name="output" as="map(*)">
<xsl:apply-templates select="$dsl" mode="interpret"/>
</xsl:variable>
this works, I'll mark the other answer as correct though there is a 2nd issue where you have to 'cast' the attribute to a string
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
version="3.0">
<xsl:output method="json" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="dsl" as="element()">
<epg lastBuildDate="10/4/2019 9:46:00 AM">
</epg>
</xsl:variable>
<xsl:variable name="output" as="map(*)*">
<xsl:apply-templates select="$dsl" mode="interpret"/>
</xsl:variable>
<xsl:sequence select="$output"/>
</xsl:template>
<xsl:template match="epg" as="map(*)" mode="interpret">
<xsl:sequence select="map {
'lastBuildDate' : #lastBuildDate => xs:string()
}"/>
</xsl:template>
</xsl:stylesheet>

What's the best way to move space-delimited tokens from one attribute to another in XSLT-2.0?

I'm trying to move space-delimited tokens from one attribute to another in XSLT-2.0. For example, given
<!-- SOURCE DOCUMENT -->
<?xml version="1.0" encoding="UTF-8"?>
<root>
<p class="foo"/>
<p class="foo bar baz"/>
<p class="foo bar baz" outputclass="BAR"/>
<p class="foo bar baz" outputclass="BAR HELLO"/>
</root>
I need to move #class="foo" to #outputclass="FOO" and #class="bar" to #outputclass="BAR", deleting the source attribute if it becomes empty and augmenting the target attribute if it exists (simple token-set operations):
<!-- RESULTING DOCUMENT -->
<?xml version="1.0" encoding="UTF-8"?>
<root>
<p outputclass="FOO"/>
<p class="baz" outputclass="FOO BAR"/>
<p class="baz" outputclass="FOO BAR"/>
<p class="baz" outputclass="FOO BAR HELLO"/>
</root>
I think I have everything figured out except the actual token-moving part. Every direction I go down ends up complicated and broken, and I feel like XSLT-2.0 surely has a simple approach that I'm missing.
Here's what I have so far:
<?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"
xmlns:mine="mine:local"
exclude-result-prefixes="xs"
version="2.0">
<!-- baseline identity transform -->
<!-- (for non-elements - attributes, whitespace PCDATA, etc.) -->
<xsl:template match="#*|(node() except *)">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- for element nodes, remap attributes then copy element -->
<xsl:template match="*">
<!-- get original attribute sequence -->
<xsl:variable name="atts1" select="#*"/>
<!-- use our function to remap two attribute tokens -->
<xsl:variable name="atts2" select="mine:remap($atts1, 'class', 'foo', 'outputclass', 'FOO')"/>
<xsl:variable name="atts3" select="mine:remap($atts2, 'class', 'bar', 'outputclass', 'BAR')"/>
<!-- stuff updated attribute sequence into element -->
<xsl:copy>
<xsl:sequence select="$atts3"/>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
<!-- remap #from_att~="$from_token" to #to_att~="$to_token" -->
<xsl:function name="mine:remap">
<xsl:param name="orig_atts"/>
<xsl:param name="from_att"/>
<xsl:param name="from_token"/>
<xsl:param name="to_att"/>
<xsl:param name="to_token"/>
<!-- ******** TOKEN-MOVING MAGIC!?! ******** -->
<xsl:sequence select="$orig_atts"/>
</xsl:function>
</xsl:stylesheet>
Basically I need to figure out how TOKEN-MOVING MAGIC!?! can move a single token (including deletion of empty "from" attributes). I've searched quite a bit but I haven't seen this particular problem covered.
Edit: The number and names of attributes to remap can be anything, and their values are case-sensitive. It's the magic inside the mine:remap function to remap a single value in an attribute sequence that I'm looking for.
Edit: The reason for approaching attribute modification with a function is that we have a number of different token remappings to apply to different files, and I hoped to allow our non-XSLT-savvy users to easily adjust the remappings to their needs. I was unable to figure out how to provide similar generalization with a template-matching-based approach.
Thanks!
Here is a short XSLT 2.0 solution (just 26 lines):
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="p/#class[tokenize(., ' ') = ('foo', 'bar')]">
<xsl:if test="tokenize(., ' ')[not(. = ('foo', 'bar'))]">
<xsl:attribute name="class"
select="string-join(tokenize(., ' ')[not(. = ('foo', 'bar'))], ' ')"/>
</xsl:if>
<xsl:attribute name="outputclass" select=
"upper-case(string-join(
(
tokenize(., ' ')[. = ('foo', 'bar')],
tokenize(../#outputclass, ' ')
[not(lower-case(.) = tokenize(current(), ' '))]
),
' '
)
)"/>
</xsl:template>
<xsl:template match="p/#outputclass[../#class[tokenize(., ' ') = ('foo', 'bar')]]"/>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<root>
<p class="foo"/>
<p class="foo bar baz"/>
<p class="foo bar baz" outputclass="BAR"/>
<p class="foo bar baz" outputclass="BAR HELLO"/>
</root>
the wanted, correct result is produced:
<root>
<p outputclass="FOO"/>
<p class="baz" outputclass="FOO BAR"/>
<p class="baz" outputclass="FOO BAR"/>
<p class="baz" outputclass="FOO BAR HELLO"/>
</root>
Update:
Here is the same transformation with almost everything parameterized, as requested in a comment by the OP, just 32 lines:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="pfromName" select="'class'"/>
<xsl:param name="ptoName" select="'outputclass'"/>
<xsl:param name="pTokens" select="'foo', 'bar'"/>
<xsl:param name="pnewNames" select="'FOO', 'BAR'"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="p/#*[name() = $pfromName][tokenize(., ' ') = $pTokens]">
<xsl:if test="tokenize(., ' ')[not(. = $pTokens)]">
<xsl:attribute name="{$pfromName}"
select="string-join(tokenize(., ' ')[not(. = $pTokens)], ' ')"/>
</xsl:if>
<xsl:attribute name="{$ptoName}" select=
"upper-case(string-join(
(
tokenize(., ' ')[. = $pTokens],
tokenize(../#*[name()=$ptoName], ' ')
[not(lower-case(.) = tokenize(current(), ' '))]
),
' '
)
)"/>
</xsl:template>
<xsl:template
match="p/#*[name()=$ptoName][../#*[name()=$pfromName][tokenize(., ' ') = $pTokens]]"/>
</xsl:stylesheet>
Update2:
Here is a completely parameterized XSLT 2.0 transformation (not using the upper-case() and lower-case() functions), just 37 lines:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="pfromName" select="'class'"/>
<xsl:param name="ptoName" select="'outputclass'"/>
<xsl:param name="pTokens" select="'foo', 'bar'"/>
<xsl:param name="pnewNames" select="'FOO', 'BAR'"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="p/#*[name() = $pfromName][tokenize(., ' ') = $pTokens]">
<xsl:if test="tokenize(., ' ')[not(. = $pTokens)]">
<xsl:attribute name="{$pfromName}"
select="string-join(tokenize(., ' ')[not(. = $pTokens)], ' ')"/>
</xsl:if>
<xsl:attribute name="{$ptoName}" select=
"string-join(
distinct-values(
(for $token in tokenize(., ' ')[. = $pTokens],
$n in 1 to count($pTokens),
$ind in $n[$token eq $pTokens[$n]]
return $pnewNames[$ind]
,
tokenize(../#*[name()=$ptoName], ' ')
)
),
' '
)
"/>
</xsl:template>
<xsl:template
match="p/#*[name()=$ptoName][../#*[name()=$pfromName][tokenize(., ' ') = $pTokens]]"/>
</xsl:stylesheet>
In the following sample I have tried to delegate as much as possible to templates:
<?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"
exclude-result-prefixes="#all"
version="3.0">
<xsl:param name="tokens" as="xs:string*"
select="'foo', 'bar'"/>
<xsl:param name="collation" as="xs:string">http://www.w3.org/2005/xpath-functions/collation/html-ascii-case-insensitive</xsl:param>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="*[#class][exists($tokens[contains-token(current()/#class, ., $collation)])]">
<xsl:copy>
<xsl:variable name="new-att" as="attribute()">
<xsl:attribute name="outputclass"/>
</xsl:variable>
<xsl:apply-templates select="#*, $new-att[not(current()/#outputclass)]">
<xsl:with-param name="tokens-found"
select="$tokens[contains-token(current()/#class, ., $collation)]"/>
</xsl:apply-templates>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="#class">
<xsl:param name="tokens-found"/>
<xsl:variable name="remaining-tokens" select="tokenize(., ' ')[not(. = $tokens-found)]"/>
<xsl:if test="exists($remaining-tokens)">
<xsl:attribute name="{name()}" select="$remaining-tokens"/>
</xsl:if>
</xsl:template>
<xsl:template match="#outputclass">
<xsl:param name="tokens-found"/>
<xsl:variable name="new-tokens" select="$tokens-found[not(contains-token(current(), ., $collation))]"/>
<xsl:attribute name="{name()}" select="$new-tokens, ."/>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/bEzkTcx/1
I haven't implemented the upper-case transformation of the tokens to be moved, I guess it should be easy to add that.
The code uses XSLT 3 with XPath 3 and the function https://www.w3.org/TR/xpath-functions/#func-contains-token but it has a definition in the spec that you could use in a user-defined XSLT 2 function. It is of course also easy to not declare the identity transformation using xsl:mode but by spelling it out.
XSLT 3 is available with Saxon 9.8 or later for Java and .NET, with Saxon-C for C/C++, with bindings for PHP and Python and with Saxon-JS 2 inside of modern web browsers and for Node.js.
Here is what I ended up with for the mine:remap() function:
<!-- remap #from_att~="$from_token" to #to_att~="$to_token" -->
<xsl:function name="mine:remap">
<xsl:param name="orig_atts" as="attribute()*"/>
<xsl:param name="from_att"/>
<xsl:param name="from_token"/>
<xsl:param name="to_att"/>
<xsl:param name="to_token"/>
<!-- get tokenized list of values of "from" attributes -->
<xsl:variable name="from_att_values" select="tokenize($orig_atts[name() = $from_att], ' ')"/>
<xsl:choose>
<!-- does the "from" attribute contain our value to replace? -->
<xsl:when test="$from_att_values = $from_token">
<!-- if so, iterate through attributes to preserve their order -->
<xsl:for-each select="$orig_atts">
<xsl:choose>
<!-- if "from" and "to" attributes are the same, replace $from_token with $to_token in-place -->
<xsl:when test="(name(.) = $from_att) and ($from_att = $to_att)">
<xsl:attribute name="{name(.)}" select="for $t in $from_att_values
return ($t[$t != $from_token], $to_token[$t = $from_token])"/>
</xsl:when>
<!-- if "from" attribute, define with $from_token value removed -->
<xsl:when test="name(.) = $from_att">
<xsl:variable name="new_from_att_values" select="$from_att_values[not(. = $from_token)]"/>
<xsl:if test="count($new_from_att_values) > 0">
<xsl:attribute name="{$from_att}" select="$new_from_att_values"/>
</xsl:if>
</xsl:when>
<!-- if "to" attribute, define with $to_token value added -->
<xsl:when test="name(.) = $to_att">
<xsl:attribute name="{$to_att}" select="distinct-values((tokenize(., ' '), $to_token))"/>
</xsl:when>
<xsl:otherwise>
<xsl:copy/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<!-- if there was no "from" attribute to modify above, create it here -->
<xsl:if test="not($orig_atts[name() = $to_att])">
<xsl:attribute name="{$to_att}" select="$to_token"/>
</xsl:if>
</xsl:when>
<!-- if not, return original attributes -->
<xsl:otherwise>
<xsl:sequence select="$orig_atts"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
I iterate through the attributes to preserve their order, then I use xsl:choose to handle the from (remove a token), to (add a token), or other (copy) attributes.

access temporary tree when transformation not finished

I have a two phases XSLT transformation with xml as output. When I set a breakpoint in one of templates during my first phase and start my xslt transformation in debug mode with XML Spy Professional 2020, I can see an xml structure in XSL Output.xml as the processed result before my template with breakpoint is applied.
My question is, is there a way in one template in the same phase to access this structure, which is a temporary result of transformation, which is not yet completed?
For development I use XML Spy Professional 2020 and for transformation in application I use Saxon Professional Edition SaxonPE9-9-1-3J.
My problem is following:
Input is a plain text https://gist.github.com/jia2/35143e79213864153b57ad0323a440a8#file-input-txt
Based on this format rules https://gist.github.com/jia2/76d676b90935cb7f33f5028180557af3,
the expected XML output like this:
https://gist.github.com/jia2/daaa4b2de5d1dadcb834f9f91c65d45b
Here my template:
<?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" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:csb="http://www.dbcargo.org/csb" exclude-result-prefixes="#all" version="3.0">
<!-- <xsl:param name="msg" as="xs:string">H0 EVU_DBSRD PVG Z24 ABF-RF IR ExternalPartnerID_uuuuuuuuuuuuuuuuu0202017-03-16-07.27.40.864320NJNJ M1 80281261300008 M2 16.03.201707:27:00Z1 H62430 16.03.2017 16.03.201707:00:00+0027R1 00131800820664780201703154023641201703151159043706346965 000 JJ R1 02031800819657480201703154045545201703151159306557346965 000 NN </xsl:param> -->
<xsl:param name="msg" as="xs:string">H0 EVU_DBSRD PVG Z24 ABF-RF IR ExternalPartnerID_uuuuuuuuuuuuuuuuu0202017-03-16-07.27.40.864320NJNJJJ M1 80281261300008 M2 16.03.201707:27:00Z1 H62430 16.03.2017 16.03.201707:00:00+0027R1 00131800820664780201703154023641201703151159043706346965 000 JJ R1 02031800819657480201703154045545201703151159306557346965 000 NN </xsl:param>
<xsl:param name="relatviePath2MFL" as="xs:string" select="'./format.xml'"/>
<xsl:variable name="MFL" select="document($relatviePath2MFL)"/>
<xsl:output method="xml" indent="yes"/>
<xsl:mode name="unroll" on-no-match="shallow-copy"/>
<xsl:strip-space elements="*"/>
<xsl:template match="StructFormat[#repeat]" mode="unroll">
<xsl:variable name="this" select="."/>
<xsl:choose>
<xsl:when test="$this/#repeat != '*' ">
<xsl:for-each select="1 to #repeat">
<xsl:choose>
<xsl:when test="$this/#delimOptional = 'n' and $this/TagField and contains($msg, $this/TagField)">
<xsl:copy select="$this">
<xsl:apply-templates select="#* except #repeat, node()" mode="#current"/>
</xsl:copy>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="repeat" select="count(tokenize($msg, $this/TagField/#value)) - 1"/>
<xsl:for-each select="1 to $repeat">
<xsl:copy select="$this">
<xsl:apply-templates select="#* except #repeat, node()" mode="#current"/>
</xsl:copy>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="StructFormat[not(#repeat)]" mode="unroll">
<xsl:variable name="this" select="."/>
<xsl:choose>
<xsl:when test="$this/TagField and not(contains($msg, $this/TagField/#value)) ">
</xsl:when>
<xsl:otherwise>
<xsl:copy select="$this">
<xsl:apply-templates select="#* except #repeat, node()" mode="#current"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="FieldFormat[#repeat]" mode="unroll">
<xsl:variable name="this" select="."/>
<xsl:for-each select="1 to #repeat">
<xsl:copy select="$this">
<xsl:apply-templates select="#* except #repeat, node()" mode="#current"/>
</xsl:copy>
</xsl:for-each>
</xsl:template>
<xsl:variable name="complete-struct">
<xsl:apply-templates select="$MFL/*" mode="unroll"/>
</xsl:variable>
<xsl:template match="/">
<xsl:element name="{$MFL/MessageFormat/#name}">
<xsl:apply-templates select="$complete-struct/*"/>
</xsl:element>
</xsl:template>
<xsl:template match="StructFormat">
<xsl:element name="{#name}">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="FieldFormat">
<xsl:variable name="precedingFieldFormatsLength" select="sum(preceding::FieldFormat/#length)"/>
<xsl:variable name="offset">
<xsl:value-of select="string-length(string-join(./preceding::TagField/#value, ''))"/>
</xsl:variable>
<xsl:element name="{#name}">
<xsl:variable name="value" select="substring($msg, 1 + $precedingFieldFormatsLength + $offset, #length)"/>
<xsl:value-of select="csb:formatField(.,$value)"/>
</xsl:element>
</xsl:template>
<!-- format output -->
<xsl:function name="csb:formatField" as="xs:string">
<xsl:param name="field" as="element()"/>
<xsl:param name="value" as="xs:string"/>
<xsl:choose>
<xsl:when test="$field/#length = '1' and $value = ' '">
<xsl:value-of select="''"/>
</xsl:when>
<!-- remove leading and trailing space -->
<xsl:when test="$field/#trimLeading = ' ' and $field/#trimTrailing = ' '">
<xsl:value-of select="fn:replace($value, '^\s+|\s+$', '')"/>
</xsl:when>
<!-- remove ONLY leading space -->
<xsl:when test="$field/#trimLeading = ' ' and fn:not(fn:exists($field//#trimTrailing))">
<xsl:value-of select="fn:replace($value, '^\s+', '')"/>
</xsl:when>
<!-- remove ONLY trailing space -->
<xsl:when test="$field/#trimTrailing = ' ' and fn:not(fn:exists($field//#trimLeading))">
<xsl:value-of select="fn:replace($value, '\s+$', '')"/>
</xsl:when>
<!-- remove leading 0 -->
<xsl:when test="$field/#type = 'Numeric' and $field/#trimLeading = '0' and fn:not(fn:exists($field//#trimTrailing))">
<!-- <xsl:value-of select="fn:replace($value, '^0+', '')"/> -->
<xsl:if test="number($value) != number($value)">
<xsl:message terminate="yes" ><xsl:value-of select="concat('Transformation failed. The field', $field, ' has invalid value')" /></xsl:message>
</xsl:if>
<xsl:value-of select="number($value)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$value"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
</xsl:stylesheet>
My xslt template https://gist.github.com/jia2/5f7387e549e6f83601dbfac23ceb3acf is reading this format.xml as input and the plain text is passed by as parameter. It works for some inputs. but it will fail, when the value, which is used for marking begin of "StructFormat", exist in other positions.
For example:
<StructFormat name='HandoverTakeover' delimOptional='n' optional='y'>
<TagField type='String' value='U1 '/>
This means, StructFormat should be generated, when the input has "U1 " at a position. Now I'm just checking if input text contains "U1 " (<xsl:when test="$this/TagField and not(contains($msg, $this/TagField/#value)) ">), but this is not enough, I need to check, if the "U1 " comes in the "right" position range, not in the whole input.
I though if I can access currently build result tree, I can count the length until now to to cut the text before this position where I am checking.
Thanks
Dingjun
XSLT is a functional language; it therefore disallows operations whose result would depend on the order of execution. The fact that particular processors organise the processing in a particular way (even when two different processors choose the same strategy) doesn't mean it is something that can be relied on; in a few years time, for example, parallel execution strategies may be much more common.
More specifically, the fact that the two phases of your transformation are executing "concurrently" (one starts before the other finishes) is an internal optimization that you cannot exploit or rely on, and this is by design.
No doubt the transformation you are trying to effect can be achieved in some completely different way within the paradigm of a declarative functional language. I haven't studied the particular problem; like many people answering questions on StackOverflow, I'm not prepared to follow links to code that's off-site.

Xslt Concatenate two nodelists with seperator

I am trying to concatenate two list of nodes which have child elements with a string-join but I am loosing the xml tags in the child elements
Input:
node1: hello I am trying <abc> some </abc> xslt code
node2: but not working
expected output
hello I am trying <abc> some </abc> xslt code, but not working
If you have a sequence of two element nodes containing the content you have shown and you want to create an output with a separator , between those nodes then one way would be to push the elements through a template that adds the separator:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="root">
<xsl:param name="seq1" select="node1, node2"/>
<xsl:copy>
<xsl:apply-templates select="$seq1"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node1 | node2">
<xsl:if test="position() > 1">, </xsl:if>
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/pPJ8LVh
If you want to work on a string level with string-join then you first need to serialize the contents e.g.
<xsl:output method="text"/>
<xsl:template match="root">
<xsl:param name="seq1" select="node1, node2"/>
<xsl:copy>
<xsl:value-of select="$seq1 ! serialize(node())" separator=", "/>
</xsl:copy>
</xsl:template>
https://xsltfiddle.liberty-development.net/pPJ8LVh/1
<xsl:output method="text"/>
<xsl:template match="root">
<xsl:param name="seq1" select="node1, node2"/>
<xsl:variable name="str1" select="string-join($seq1 ! serialize(node()), ', ')"/>
<xsl:copy>
<xsl:value-of select="$str1"/>
</xsl:copy>
</xsl:template>
https://xsltfiddle.liberty-development.net/pPJ8LVh/2
As you can see, the last two examples create strings output as text, it seems more likely that you want to create a result nodes, like done in the first suggestion.

Select part of String in XSLT

I want to select part of a String using xslt.
Input :
p:endnote_bl1
p:endnote_bl2
p:endnote_bl3
p:endnote_bl4
Output Should be :
endnote_bl1
endnote_bl2
endnote_bl3
endnote_bl4
Can I do this using split or something else? I am using XSLT 2.0
Use replace function
replace('p:endnote_bl1', '^[^:]*:', '')
You could do simply:
substring-after($string, ':')
There are a multitude of ways to do this.
This transformation shows four different solutions, the first two of which are XPath 1.0 (XSLT 1.0) solutions:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="vSolution1">
<xsl:apply-templates select="/*" mode="substring-after"/>
</xsl:variable>
<xsl:variable name="vSolution2">
<xsl:apply-templates select="/*" mode="substring"/>
</xsl:variable>
<xsl:variable name="vSolution3">
<xsl:apply-templates select="/*" mode="split"/>
</xsl:variable>
<xsl:variable name="vSolution4">
<xsl:apply-templates select="/*" mode="replace"/>
</xsl:variable>
<xsl:template match="/">
<xsl:copy-of select="$vSolution1"/>
==================
<xsl:copy-of select="$vSolution2"/>
==================
<xsl:copy-of select="$vSolution3"/>
==================
<xsl:copy-of select="$vSolution4"/>
</xsl:template>
<xsl:template match="node()|#*" mode="#all">
<xsl:copy>
<xsl:apply-templates select="node()|#*" mode="#current"/>
</xsl:copy>
</xsl:template>
<xsl:template match="s/text()" mode="substring-after">
<xsl:value-of select="substring-after(., 'p:')"/>
</xsl:template>
<xsl:template match="s/text()" mode="substring">
<xsl:value-of select="substring(., 3)"/>
</xsl:template>
<xsl:template match="s/text()" mode="split">
<xsl:value-of select="tokenize(., ':')[2]"/>
</xsl:template>
<xsl:template match="s/text()" mode="replace">
<xsl:value-of select="replace(., '^.+:', '')"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the XML document below (none was provided!):
<t>
<s>p:endnote_bl1</s>
<s>p:endnote_bl2</s>
<s>p:endnote_bl3</s>
<s>p:endnote_bl4</s>
</t>
We get four identical, correct results, each produced by one of the four solutions:
<t>
<s>endnote_bl1</s>
<s>endnote_bl2</s>
<s>endnote_bl3</s>
<s>endnote_bl4</s>
</t>
==================
<t>
<s>endnote_bl1</s>
<s>endnote_bl2</s>
<s>endnote_bl3</s>
<s>endnote_bl4</s>
</t>
==================
<t>
<s>endnote_bl1</s>
<s>endnote_bl2</s>
<s>endnote_bl3</s>
<s>endnote_bl4</s>
</t>
==================
<t>
<s>endnote_bl1</s>
<s>endnote_bl2</s>
<s>endnote_bl3</s>
<s>endnote_bl4</s>
</t>

Resources