fluffywarthog
fluffywarthog

Reputation: 61

Recursive loop, multiple conditions in XSL

I have an XML document with multiple instances and types of children within an element type, and need to display them both in order and with different formatting for each child type.

I've tried different combinations of for-each and conditional choose loops, but with no luck.

XML Segment:

<PLAY>  
<PERSONAE>
<TITLE>Dramatis Personae</TITLE>

<PERSONA>CLAUDIUS, king of Denmark. </PERSONA>
<PERSONA>HAMLET, son to the late, and nephew to the present king.</PERSONA>
<PERSONA>POLONIUS, lord chamberlain. </PERSONA>
<PERSONA>HORATIO, friend to Hamlet.</PERSONA>
<PERSONA>LAERTES, son to Polonius.</PERSONA>
<PERSONA>LUCIANUS, nephew to the king.</PERSONA>

<PGROUP>
<PERSONA>VOLTIMAND</PERSONA>
<PERSONA>CORNELIUS</PERSONA>
<PERSONA>ROSENCRANTZ</PERSONA>
<PERSONA>GUILDENSTERN</PERSONA>
<PERSONA>OSRIC</PERSONA>
<GRPDESCR>courtiers.</GRPDESCR>
</PGROUP>

<PERSONA>A Gentleman</PERSONA>
<PERSONA>A Priest. </PERSONA>

<PGROUP>
<PERSONA>MARCELLUS</PERSONA>
<PERSONA>BERNARDO</PERSONA>
<GRPDESCR>officers.</GRPDESCR>
</PGROUP>

XSL:

<h2><xsl:value-of select="PLAY/PERSONAE/TITLE" /></h2>
<xsl:for-each select="PLAY/PERSONAE/node()">
    <xsl:choose>
        <xsl:when test="PERSONA">
            <xsl:value-of select="PERSONA" />
        </xsl:when>
        <xsl:when test="PGROUP">
            <xsl:for-each select="PERSONA">
                <xsl:value-of select="PERSONA" />
            </xsl:for-each>
            <xsl:value-of select="PGROUP" />
        </xsl:when>
        <xsl:otherwise />
    </xsl:choose>
</xsl:for-each> 

Output

Dramatis Personae
VOLTIMANDMARCELLUS

Expected (Hopeful) Output

Dramatis Personae

CLAUDIUS, king of Denmark.

HAMLET, son to the late, and nephew to the present king.

POLONIUS, lord chamberlain.
HORATIO, friend to Hamlet.
LAERTES, son to Polonius.
LUCIANUS, nephew to the king.

VOLTIMAND
CORNELIUS
ROSENCRANTZ
GUILDENSTERN
OSRIC

A Gentleman
A Priest.

MARCELLUS
BERNARDO


FRANCISCO, a soldier.
REYNALDO, servant to Polonius.
Players.
Two Clowns, grave-diggers.
FORTINBRAS, prince of Norway.
A Captain.
English Ambassadors.
GERTRUDE, queen of Denmark, and mother to Hamlet.
OPHELIA, daughter to Polonius.

Lords, Ladies, Officers, Soldiers, Sailors, Messengers, and other Attendants.

Ghost of Hamlet's Father.

Every time, it either returns only the very first PERSONA child it parses, or it returns the first two PGROUP/PERSONA children. Can anyone see what I'm doing wrong?

Upvotes: 0

Views: 156

Answers (2)

michael.hor257k
michael.hor257k

Reputation: 116993

If I am guessing correctly, you want an HTML output, not text. Here's a way you could achieve a result that, when rendered, will look like what you posted:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="utf-8"/>

<xsl:template match="/PLAY">
    <body>
        <xsl:apply-templates/>
    </body>
</xsl:template> 

<xsl:template match="PERSONAE">
    <h2>
        <xsl:value-of select="TITLE" />
    </h2>
    <div>
        <xsl:apply-templates select="PERSONA | PGROUP"/>
    </div>
</xsl:template> 

<xsl:template match="PERSONA">
    <xsl:value-of select="." />
    <br/>
</xsl:template> 

<xsl:template match="PGROUP">
    <p>
        <xsl:apply-templates select="PERSONA"/>
    </p>
</xsl:template> 

</xsl:stylesheet>

When applied to the following test input:

<PLAY>
   <PERSONAE>
      <TITLE>Dramatis Personae</TITLE>
      <PERSONA>CLAUDIUS, king of Denmark. </PERSONA>
      <PERSONA>HAMLET, son to the late, and nephew to the present king.</PERSONA>
      <PERSONA>POLONIUS, lord chamberlain. </PERSONA>
      <PERSONA>HORATIO, friend to Hamlet.</PERSONA>
      <PERSONA>LAERTES, son to Polonius.</PERSONA>
      <PERSONA>LUCIANUS, nephew to the king.</PERSONA>
      <PGROUP>
         <PERSONA>VOLTIMAND</PERSONA>
         <PERSONA>CORNELIUS</PERSONA>
         <PERSONA>ROSENCRANTZ</PERSONA>
         <PERSONA>GUILDENSTERN</PERSONA>
         <PERSONA>OSRIC</PERSONA>
         <GRPDESCR>courtiers.</GRPDESCR>
      </PGROUP>
      <PERSONA>A Gentleman</PERSONA>
      <PERSONA>A Priest. </PERSONA>
      <PGROUP>
         <PERSONA>MARCELLUS</PERSONA>
         <PERSONA>BERNARDO</PERSONA>
         <GRPDESCR>officers.</GRPDESCR>
      </PGROUP>
   </PERSONAE>
</PLAY>

the result will be:

<body>
   <h2>Dramatis Personae</h2>
<p>CLAUDIUS, king of Denmark. <br>HAMLET, son to the late, and nephew to the present king.<br>POLONIUS, lord chamberlain. <br>HORATIO, friend to Hamlet.<br>LAERTES, son to Polonius.<br>LUCIANUS, nephew to the king.<br><p>VOLTIMAND<br>CORNELIUS<br>ROSENCRANTZ<br>GUILDENSTERN<br>OSRIC<br></p>A Gentleman<br>A Priest. <br><p>MARCELLUS<br>BERNARDO<br></p></p>
</body>

rendered as:

enter image description here

Upvotes: 1

bknights
bknights

Reputation: 15377

You probably want something more like:

<xsl:template match="PERSONAE">
<xsl:value-of select="TITLE"/>
    <xsl:apply-templates select="PERSONA|PGROUP"/>
</xsl:template>

<xsl:template match="PGROUP">
<xsl:apply-templates select="PERSONA"/>
</xsl:template>

<xsl:template match="PERSONA">
<xsl:value-of select="."/>
</xsl:template>

Your:

<xsl:for-each select="PLAY/PERSONAE/node()">

is a just selecting the first node child of each PERSONAE

Upvotes: 1

Related Questions