Question

Using Apache FOP, I want to collect some info in a PDF file. The XML source has some child nodes a to e, let's say

<node>
   <a>some val</a>
   <b>some other val</b>
   <c>more val</c>
   <d>even more val</d>
   <e>a last val</e>
</node>

I don't want to display all of them. a,b,c shall always be displayed but may be emtpy. The maximum amount of displayed values is 3. So, d and e are optional and must be kept in that order.

Sadly, the XML structure cannot be modified.

What is the right XSLT for that? I tried

<xsl:for-each select="child::*[name()='a' or name() = 'b' or name() = 'c' or name() = 'd' or name() = 'e'][string-length(.)&gt;0]">
    <xsl:if test="position() &lt;= 3">
        <xsl:value-of select="name()"/>
    </xsl:if>
</xsl:for-each>

but that doesn't bring me an ordered list. :(

Was it helpful?

Solution

<xsl:sort /> should hep.

In your case it would be:

<xsl:sort select="name()"/>

Therefore try:

    <xsl:for-each select="child::*[name()='a' or name() = 'b' or name() = 'c' or name() = 'd' or name() = 'e'][string-length(.)&gt;0]">
        <xsl:sort select="name()"/>
        <xsl:if test="position() &lt;= 3">
            <xsl:value-of select="name()"/>
        </xsl:if>
    </xsl:for-each>

Update: Because in real live input XML there is not useful information to sort by you may add some meta information. Where to store the meta information depend on the capabilities of the xslt processor.

If you can use the node-set() extension you may try something like this:

Add a variable to stylesheet with expected order.

xsl:variable name="myOrder">
        <order name="a" pos="1" />
        <order name="b" pos="3" />
        <order name="c" pos="2" />
        <order name="d" pos="4" />
        <order name="e" pos="5" />
    </xsl:variable>

Make this variable usable as node-set by:

<xsl:variable name="Order" select="exsl:node-set($myOrder)" />

Sort with help of this variable.

<xsl:sort select="$Order/order[@name= name(current())]/@pos"/>

OTHER TIPS

@Florian Ruh, your stated requirement is not self-consistent: "I don't want to display all of them. a,b,c shall always be displayed. The maximum amount of displayed values is 3. So, d and e are optional and must be kept in that order."

If a, b and c are always displayed, and the maximum number of displayed values is 3, then there is no chance for d and 3 to be displayed.

Please clarify your requirement.

Note that it is very poor form to use the name() function as you have.

The equivalent to:

<xsl:for-each select="child::*[name()='a' or name() = 'b' or name() = 'c' or name() = 'd' or name() = 'e'][string-length(.)&gt;0]">

is:

<xsl:for-each select="(a|b|c|d|e)[string(.)]">

... and the approach I posit is namespace-safe, while the approach you used is not.

Perhaps I am not understanding correctly, but if you want to only return the first three items into an ordered list, you could do something like

<xsl:template match="node">
<fo:list-block>
<xsl:apply-templates/>
</fo:list-block>
</xsl:template>

And

<xsl:template match="node/*[4] | node/*[5]"/>
<xsl:template match="node/*[1] | node/*[2] | node/*[3]">
<fo:list-item>
<fo:list-item-label>
<fo:block><xsl:value-of select="position()"/></fo:block>
</fo:list-item-label>
<fo:list-item-body>
<fo:block><xsl:value-of select="."/></fo:block>
</fo:list-item-body>
</fo:list-item>
</xsl:template>

This will ignore any 4th and 5th elements, but process the first, second, and third in document order (since that's the order in which they are encountered).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top