0

I need to transform a XML to XHTML. Within the XML are multiple paragraphs and embedded quotations e.g.

<para>SomeText</para>
<para><quote>SomeText</quote></para>
<para>SomeText</para>

I tried this:

<xsl:choose>
    <xsl:when test="//text/para">
        <xsl:for-each select="//text">
            <xsl:for-each select="//para">
                <p><xsl:value-of select="text()"/></p>
            </xsl:for-each>
        </xsl:for-each>
    </xsl:when>
    <xsl:when test="//text/para[quote]">
        <xsl:for-each select="//text">
            <xsl:for-each select="//para/quote">
                <p><q><xsl:value-of select="text()"/></q></p>
            </xsl:for-each>
        </xsl:for-each>
    </xsl:when>
</xsl:choose>

The second condition simply gets ignored however.

Hunter Turner
  • 6,804
  • 11
  • 41
  • 56
Fajeth88
  • 25
  • 4
  • You need to do some serious reading before you do any more coding. Read up on template rules, on the XPath dynamic context, and on the meaning of the "//" at the start of a path expression. – Michael Kay Jul 14 '16 at 17:25

2 Answers2

2

As @LarsH indicates, avoid <xsl:for-each>. Use template matching.

This simple transformation:

<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="para">
    <p><xsl:apply-templates /></p>
  </xsl:template>

  <xsl:template match="quote">
    <q><xsl:apply-templates /></q>
  </xsl:template>

</xsl:transform>

will turn this:

<text>
  <para>SomeText</para>
  <para><quote>SomeText</quote></para>
  <para>SomeText</para>
</text>

into

<p>SomeText</p>
<p><q>SomeText</q></p>
<p>SomeText</p>

Further reading here on SO:

Community
  • 1
  • 1
Tomalak
  • 332,285
  • 67
  • 532
  • 628
  • Thanks a lot. But I have a template that matches the root at the top for the title, meta data and stuff. If I now insert those two templates beneath it, i get this error: "Element must only be used at top level of stylesheet." – Fajeth88 Jul 14 '16 at 16:04
  • You can't nest `` (which is synonymous with ``). Move the two templates on their own. Also, it's likely that they will never execute until you add an `` somewhere in you main template. Like Lars, I recommend reading about `` first. The link in his comment and the first link in my answer provide an introduction. – Tomalak Jul 14 '16 at 16:10
1

The problem is you're using XPath expressions that ignore context (aside from the context document), i.e. expressions that start with //. So if you have any <para> element anywhere in the document that has a <quote> child element, the first condition will always be true and the second condition will never be reached.

Really you want to move the for-each loop (or probably better, a set of templates with match patterns and apply-templates) outside of the choose/when conditions. In fact you will probably not need a choose/when at all, once you have the right templates.

(Moving in some info from comments)

XSLT and XPath are sufficiently complex that it's really worth learning the basics before trying to get something working by trial-and-error. You'll save a lot of time that way. For a general introduction I would recommend

Then for more advanced discussion of xsl:for-each vs. xsl:apply-templates, see

Community
  • 1
  • 1
LarsH
  • 27,481
  • 8
  • 94
  • 152
  • Thx, I'll try that. After I figure out how anyway - this is the first time I work with XML/HTML. – Fajeth88 Jul 14 '16 at 15:56
  • 1
    @Fajeth88: It really is worth learning the basics before doing much trial-and-error. XSLT/XPath are sufficiently complex even if you understand them. Once you've read a tutorial on for-each vs. apply-templates, try http://stackoverflow.com/questions/4460232/differences-between-for-each-and-templates-in-xsl – LarsH Jul 14 '16 at 16:00