XSL question

R

Rhino

I'm pretty new to XSL. I've only played with it for a few hours very
sporadically over the course of the last few years.

I'm trying to write an XSL that will properly handle a repeating group in my
data but it's not working for me.

Basically, my data consists of 8 unique values, then a single value that can
repeat anywhere from 0 to, say, 50 times. The XML is like this:
<log>
<record>
<timestamp>timestamp-value</timestamp>
<sequence>sequence-value</sequence>
<logger>logger-value</logger>
<level>level-value</level>
<class>class-value</class>
<method>method-value</method>
<thread>thread-value</thread>
<message>message-value</message>
</record>
<record>
<timestamp>timestamp-value</timestamp>
<sequence>sequence-value</sequence>
<logger>logger-value</logger>
<level>level-value</level>
<class>class-value</class>
<method>method-value</method>
<thread>thread-value</thread>
<message>message-value</message>
<param>param1-value</param>
<param>param2-value</param>
<param>param3-value</param>
<param>param4-value</param>
<param>param5-value</param>
</record>
</log>

Most records will be the like the first one and not have any occurrences of
<param>. The remaining records will be like the second one and have several
occurrences of <param>. 50 is not a hard limit on the number of occurrences
of <param> - I'm not sure if there IS any hard limit - but it will be rare
to have more than 50. But I want my code to work for any number of
occurrences even if that is over 50. I'm just saying 50 to give you a sense
of the number of occurrences you typically see.

I want my XSL to create HTML with all records in a table that contains 9
columns: timestamp, sequence, logger, level, class, method, thread, message,
and param.

For records that have no <param> tags, I want the first 8 columns of the
table to contain the specific values of <timestamp> through <message> from
that record. The 9th column of the table should simply be empty.

For records that have <param> tags, I want one row in the table for each
occurrence of a <param> tag in that record. In other words, for the second
record described above, I want to generate five rows in the HTML table
because there are five <param> values. The first of those rows should
contain the <timestamp> thrrough <message> values in the first 8 columns and
the first <param> value in the 9th column. The second row for that record
should contain a single text string like "see above" that spans the first 8
columns with the second <param> record in the 9th column. The third through
nth rows should follow the same pattern as the second row except that they
will show the next <param> value (the third <param> value on the 3rd row,
the 4th <param> value on the 4th row etc.

I've got everything except the repeating group (the <params>) working but
I'm hitting a wall with that. Nothing I try works. It seems clear that I
need a for-each loop on param but I'm not having any luck getting it to
display the different param values.

Can anyone show me the technique I need?
 
A

Alain Ketterlin

Rhino said:
<log> [...]
<record>
<timestamp>timestamp-value</timestamp>
<sequence>sequence-value</sequence>
<logger>logger-value</logger>
<level>level-value</level>
<class>class-value</class>
<method>method-value</method>
<thread>thread-value</thread>
<message>message-value</message>
<param>param1-value</param>
<param>param2-value</param>
<param>param3-value</param>
</record>
</log>
[...]
For records that have no <param> tags, I want the first 8 columns of
the table to contain the specific values of <timestamp> through
<message> from that record. The 9th column of the table should simply
be empty.

For records that have <param> tags, I want one row in the table for
each occurrence of a <param> tag in that record. In other words, for
the second record described above, I want to generate five rows in the
HTML table because there are five <param> values. The first of those
rows should contain the <timestamp> thrrough <message> values in the
first 8 columns and the first <param> value in the 9th column. The
second [and subsequent] row for that record should contain a single
text string like "see above" that spans the first 8 columns with the
[n-th] <param> record in the 9th column.

Something like this should work:

<xsl:template match="record">
<tr>
<td><xsl:value-of select="timestamp"/></td>
...
<td><xsl:value-of select="param[1]"/></td>
</tr>
<xsl:for-each select="param[position()&gt;1]">
<tr>
<td ...>see above</td>
<td><xsl:value-of select="."/></td>
</tr>
</xsl:template>

It relies on the fact the select="param[1]" will give nothing when
there's no <param>. Likewise, the for-each won't do anything without at
least two <param>. The function position() is provided by XPath.

If you need more specific behavior, you can wrap the "optional" parts
inside <xsl:if> or <xsl:choose>. Also, using templates to process
<timestamp> etc. may be a good idea.

-- Alain.
 
R

Rhino

Alain Ketterlin said:
Rhino said:
<log> [...]
<record>
<timestamp>timestamp-value</timestamp>
<sequence>sequence-value</sequence>
<logger>logger-value</logger>
<level>level-value</level>
<class>class-value</class>
<method>method-value</method>
<thread>thread-value</thread>
<message>message-value</message>
<param>param1-value</param>
<param>param2-value</param>
<param>param3-value</param>
</record>
</log>
[...]
For records that have no <param> tags, I want the first 8 columns of
the table to contain the specific values of <timestamp> through
<message> from that record. The 9th column of the table should simply
be empty.

For records that have <param> tags, I want one row in the table for
each occurrence of a <param> tag in that record. In other words, for
the second record described above, I want to generate five rows in the
HTML table because there are five <param> values. The first of those
rows should contain the <timestamp> thrrough <message> values in the
first 8 columns and the first <param> value in the 9th column. The
second [and subsequent] row for that record should contain a single
text string like "see above" that spans the first 8 columns with the
[n-th] <param> record in the 9th column.

Something like this should work:

<xsl:template match="record">
<tr>
<td><xsl:value-of select="timestamp"/></td>
...
<td><xsl:value-of select="param[1]"/></td>
</tr>
<xsl:for-each select="param[position()&gt;1]">
<tr>
<td ...>see above</td>
<td><xsl:value-of select="."/></td>
</tr>
</xsl:template>

It relies on the fact the select="param[1]" will give nothing when
there's no <param>. Likewise, the for-each won't do anything without at
least two <param>. The function position() is provided by XPath.

If you need more specific behavior, you can wrap the "optional" parts
inside <xsl:if> or <xsl:choose>. Also, using templates to process
<timestamp> etc. may be a good idea.
Actually, there are really two fields where I show timestamp, a date and a
number of milliseconds, but I simplified it for my example. I'm already
blending <date> and <millis> to create a real timestamp and it works fine.

Tell me more about this "." construct.

I took my existing code and simply replaced 'param' in my <xsl:value-of
select="param"/> with <xsl:value-of select="."/> and starting getting my
param values. I'm guessing that once you've referred to something like
'param' in a for-each loop, you can't refer to it again the same way since
it has already been consumed in some fashion but the '.' is sort of a
synonym for the most recently referenced field so that you can get its value
again. I've never seen that technique before though and I'm not sure what
it's called so that I can Google it. If you could at least tell me its name,
I'd be able to look for more information on how it is used. Or, of course,
you can just tell me about it yourself ;-)

In any case, I should now be able to modify my code so that it does what I
want.

I'll post back on this thread if I still have trouble and I'll check back to
see what this "." technique is called....

Thanks Alain!!
 
S

Simon Wright

Rhino said:
Tell me more about this "." construct.

I took my existing code and simply replaced 'param' in my
<xsl:value-of select="param"/> with <xsl:value-of select="."/> and
starting getting my param values. I'm guessing that once you've
referred to something like 'param' in a for-each loop, you can't refer
to it again the same way since it has already been consumed in some
fashion but the '.' is sort of a synonym for the most recently
referenced field so that you can get its value again. I've never seen
that technique before though and I'm not sure what it's called so that
I can Google it. If you could at least tell me its name, I'd be able
to look for more information on how it is used. Or, of course, you can
just tell me about it yourself ;-)

The references I found by [1] include [2], which says, under "Selecting
Nodes", "." "Selects the current node".

During your for-each loop, on each pass "." refers to the current
selected <param/> node.

[1] google "xpath syntax"
[2] http://www.w3schools.com/xpath/xpath_syntax.asp
 
A

Alain Ketterlin

Simon Wright said:
Rhino said:
Tell me more about this "." construct.

I took my existing code and simply replaced 'param' in my
<xsl:value-of select="param"/> with <xsl:value-of select="."/> and
starting getting my param values. I'm guessing that once you've
referred to something like 'param' in a for-each loop, you can't refer
to it again the same way since it has already been consumed in some
fashion but the '.' is sort of a synonym for the most recently
referenced field so that you can get its value again. I've never seen
that technique before though and I'm not sure what it's called so that
I can Google it. If you could at least tell me its name, I'd be able
to look for more information on how it is used. Or, of course, you can
just tell me about it yourself ;-)

The references I found by [1] include [2], which says, under "Selecting
Nodes", "." "Selects the current node".

During your for-each loop, on each pass "." refers to the current
selected <param/> node.

[1] google "xpath syntax"
[2] http://www.w3schools.com/xpath/xpath_syntax.asp

Exactly, at any time there is a "context node". This notion is used in
XSLT, inherited from XPath. Actually, imho, W3C recommendations are the
best description I know of XPath and XSLT. I think it's a good idea to
read them at least once. I've never felt the need for any other
documentation.

http://www.w3.org/TR/xpath/
http://www.w3.org/TR/xslt

-- Alain.
 
R

Rhino

Alain Ketterlin said:
Simon Wright said:
Rhino said:
Tell me more about this "." construct.

I took my existing code and simply replaced 'param' in my
<xsl:value-of select="param"/> with <xsl:value-of select="."/> and
starting getting my param values. I'm guessing that once you've
referred to something like 'param' in a for-each loop, you can't refer
to it again the same way since it has already been consumed in some
fashion but the '.' is sort of a synonym for the most recently
referenced field so that you can get its value again. I've never seen
that technique before though and I'm not sure what it's called so that
I can Google it. If you could at least tell me its name, I'd be able
to look for more information on how it is used. Or, of course, you can
just tell me about it yourself ;-)

The references I found by [1] include [2], which says, under "Selecting
Nodes", "." "Selects the current node".

During your for-each loop, on each pass "." refers to the current
selected <param/> node.

[1] google "xpath syntax"
[2] http://www.w3schools.com/xpath/xpath_syntax.asp

Exactly, at any time there is a "context node". This notion is used in
XSLT, inherited from XPath. Actually, imho, W3C recommendations are the
best description I know of XPath and XSLT. I think it's a good idea to
read them at least once. I've never felt the need for any other
documentation.

http://www.w3.org/TR/xpath/
http://www.w3.org/TR/xslt

Thank you both, Alain and Simon. The other question I was going to ask was
about a good reference for XSLT and XPath and now you've answered that one
too ;-)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,982
Messages
2,570,186
Members
46,739
Latest member
Clint8040

Latest Threads

Top