xsl-list
[Top] [All Lists]

[xsl] For-each adds whitespace per iteration: why?

2014-01-10 11:06:18
In the context of writing an XSLT to generate DTD syntax from RNGs (for
DITA 1.3) I discovered that for-each results in whitespace being emitted
for each iteration. This came as a surprise. Reading the spec it says,
under clause 7, Repetition:

"For each item in the input sequence, evaluating the sequence constructor
<http://www.w3.org/TR/xslt20/#dt-sequence-constructor> produces a sequence
of items (see 5.7 Sequence Constructors
<http://www.w3.org/TR/xslt20/#sequence-constructors>). These output
sequences are concatenated; if item Q follows item P in the sorted
sequence, then the result of evaluating the sequence constructor with Q as
the context item is concatenated after the result of evaluating the
sequence constructor with P as the context item. The result of the
xsl:for-each <http://www.w3.org/TR/xslt20/#element-for-each> instruction
is the concatenated sequence of items.+IB0-

I understand "These output sequences are concatenated+IB0- to mean that string
concatenation rules are applied, which explains the white space.

My question: why is for-each defined in this way? It came as a surprise to
me that constructing a string using for-each resulted in extra and
unwanted whitespace (I have a function that generates strings of blanks of
a specified length so I can do indention of the DTD components and I was
getting off-by-one results in my formatted DTD output).


I tested this with this little XSLT transform:

<?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:xd="http://www.oxygenxml.com/ns/doc/xsl";
  exclude-result-prefixes="xs xd"
  version="2.0">
  
  <xsl:output method="text"/>
  
  <xsl:template name="test-for-each">
    <xsl:variable name="strings" select="('one', 'two', 'three', 'four')"/>
 value-of +ACQ-strings=<xsl:value-of select="+ACQ-strings"/>
 for +ACQ-str in +ACQ-strings return concat('/', +ACQ-str, '/')=<xsl:sequence
select="for +ACQ-str in +ACQ-strings return concat('/', +ACQ-str, '/')"/>
 string-join(+ACQ-strings, '')=<xsl:sequence select="string-join(+ACQ-strings,
'')"/>
 for-each over strings: "<xsl:for-each select="+ACQ-strings">
  <xsl:sequence select="concat('/',.,'/')"/>
</xsl:for-each>"
  </xsl:template>
  
</xsl:stylesheet>



Which produces this output using Saxon 9.5.1.2:

 value-of +ACQ-strings=one two three four
 for +ACQ-str in +ACQ-strings return concat('/', +ACQ-str, '/')=/one/ /two/ /three/
/four/
 string-join(+ACQ-strings, '')=onetwothreefour
 for-each over strings: "/one/ /two/ /three/ /four/"



I see that the for-each result is consistent with the flowr expression.

Is my analysis correct that the only way to construct a string with no
extra whitespace using a loop is to use string-join() as in my test case?

For my DTD-generation application that would mean using the for-each loop
to construct a sequence of strings and then using string-join on the
sequence to avoid additional whitespace. Of course I can simply account
for the space inserted by the concatenation and get the correct indention
and keep my code a bit simpler.

Cheers,

Eliot

-- 
Eliot Kimber
Senior Solutions Architect
"Bringing Strategy, Content, and Technology Together"
Main: 512.554.9368
www.reallysi.com
www.rsuitecms.com



--+AH4-------------------------------------------------------------------
XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: <mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--+AH4---