[XSL-LIST Mailing List Archive Home] [By Thread] [By Date]

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

Subject: [xsl] For-each adds whitespace per iteration: why?
From: Eliot Kimber <ekimber@xxxxxxxxxx>
Date: Fri, 10 Jan 2014 11:05:53 -0600

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:

+ACI-For each item in the input sequence, evaluating the sequence constructor
+ADw-http://www.w3.org/TR/xslt20/+ACM-dt-sequence-constructor+AD4- produces a
of items (see 5.7 Sequence Constructors
+ADw-http://www.w3.org/TR/xslt20/+ACM-sequence-constructors+AD4-). These
sequences are concatenated+ADs- 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 +ADw-http://www.w3.org/TR/xslt20/+ACM-element-for-each+AD4-
is the concatenated sequence of items.+IB0-

I understand +ACI-These output sequences are concatenated+IB0- to mean that
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:

+ADw-?xml version+AD0AIg-1.0+ACI- encoding+AD0AIg-UTF-8+ACI-?+AD4-
  exclude-result-prefixes+AD0AIg-xs xd+ACI-

  +ADw-xsl:output method+AD0AIg-text+ACI-/+AD4-

  +ADw-xsl:template name+AD0AIg-test-for-each+ACIAPg-
    +ADw-xsl:variable name+AD0AIg-strings+ACI- select+AD0AIg-('one', 'two',
'three', 'four')+ACI-/+AD4-
 value-of +ACQ-strings+AD0APA-xsl:value-of select+AD0AIgAk-strings+ACI-/+AD4-
 for +ACQ-str in +ACQ-strings return concat('/', +ACQ-str,
select+AD0AIg-for +ACQ-str in +ACQ-strings return concat('/', +ACQ-str,
 string-join(+ACQ-strings, '')+AD0APA-xsl:sequence
 for-each over strings: +ACIAPA-xsl:for-each select+AD0AIgAk-strings+ACIAPg-
  +ADw-xsl:sequence select+AD0AIg-concat('/',.,'/')+ACI-/+AD4-


Which produces this output using Saxon

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

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.



Eliot Kimber
Senior Solutions Architect
+ACI-Bringing Strategy, Content, and Technology Together+ACI-
Main: 512.554.9368

Current Thread