Wednesday, 8 June 2016

XML : XSL XPath select mixed text and node in order within for-each loop?

I am trying to use XSL to transform an XML document into an HTML page. I am 95% of the way there, but I am running into problems with nodes that contain both text and other nodes which must be displayed in order.

XML:

  <chapter title="Chapter 1" reqlen="2500">      <section title="The Quick Brown Fox">          <subsection title="null">              <keywords>foo,bar</keywords>              <body>                  <p>                      Lorem ipsum <b>dolor sit amet</b>, consectetur adipiscing elit.           Maecenas sed pretium nunc.                      <int>foo/bar/qux</int>                      Proin tincidunt sapien dolor, posuere varius dui efficitur ac.                      <ext>http://google.com/</ext>                  </p>                  <p>                      Foo bar <b>doqux amet</b>, foo adipiscing elit.           Maecenas sed pretium nunc.                      <int>foo/bar/qux</int>                      Proin tincidunt sapien dolor, posuere varius dui efficitur ac.                      <ext>http://google.com/</ext>                  </p>              </body>              <note>Lorem ipsum</note>          </subsection>      </section>  </chapter>    

XSL:

  <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" id="stylesheet" version="1.0">      <xsl:template match="xsl:stylesheet" />      <xsl:template match="/chapter">          <html>              <head>                  <title>                      <xsl:value-of select="@title" />                  </title>                  <style type="text/css">body {              padding: 50px 10%;            }</style>              </head>              <body>                  <h1>                      <xsl:value-of select="@title" />                  </h1>                  <xsl:for-each select="section">                      <xsl:if test="@title!='null'">                          <h2>                              <xsl:value-of select="@title" />                          </h2>                      </xsl:if>                      <xsl:for-each select="subsection">                          <xsl:if test="@title!='null'">                              <h3>                                  <xsl:value-of select="@title" />                              </h3>                          </xsl:if>                          <xsl:for-each select="body/p">                              <p>                                  <xsl:value-of select="current()" />                              </p>                          </xsl:for-each>                      </xsl:for-each>                  </xsl:for-each>              </body>          </html>      </xsl:template>  </xsl:stylesheet>    

This results in the following HTML:

  <html>      <head>          <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">          <title>Chapter 1</title>          <style type="text/css">              body {              padding: 50px 10%;              }          </style>      </head>      <body>          <h1>Chapter 1</h1>          <h2>The Quick Brown Fox</h2>          <p>              Lorem ipsum dolor sit amet, consectetur adipiscing elit.              Maecenas sed pretium nunc.              foo/bar/qux              Proin tincidunt sapien dolor, posuere varius dui efficitur ac.              http://google.com/          </p>          <p>              Foo bar doqux amet, foo adipiscing elit.              Maecenas sed pretium nunc.              foo/bar/qux              Proin tincidunt sapien dolor, posuere varius dui efficitur ac.              http://google.com/          </p>      </body>  </html>    

The problem is, I am trying to transform certain tags within the <p></p> nodes.

Inside of <p> nodes, there can be a mixture of plain text and the nodes <b></b>, <i></i>, <int></int>, <ext></ext>, and <cvc></cvc>. There will never be nesting beyond this level, i.e. a <b></b> will only ever contain text.

This is my desired HTML:

  <html>      <head>          <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">          <title>Chapter 1</title>          <style type="text/css">              body {              padding: 50px 10%;              }          </style>      </head>      <body>          <h1>Chapter 1</h1>          <h2>The Quick Brown Fox</h2>          <p>              Lorem ipsum <strong>dolor sit amet</strong>, consectetur adipiscing elit.              Maecenas sed pretium nunc.              <a href="/foo/bar/qux">foo/bar/qux</a>              Proin tincidunt sapien dolor, posuere varius dui efficitur ac.              <a href="http://google.com/">http://google.com/</a>          </p>          <p>Foo bar <strong>doqux amet</strong>, foo adipiscing elit.               Maecenas sed pretium nunc.              <a href="/foo/bar/qux">foo/bar/qux</a>              Proin tincidunt sapien dolor, posuere varius dui efficitur ac.              <a href="http://google.com/">http://google.com/</a>          </p>      </body>  </html>    

I have tried different XPath functions, for-each loops, and mixtures of the two, but I can't figure out how to get the output I want. The closest I've gotten is with this XSL:

  <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" id="stylesheet" version="1.0">      <xsl:template match="xsl:stylesheet" />      <xsl:template match="/chapter">          <html>              <head>                  <title>                      <xsl:value-of select="@title" />                  </title>                  <style type="text/css">body {              padding: 50px 10%;            }</style>              </head>              <body>                  <h1>                      <xsl:value-of select="@title" />                  </h1>                  <xsl:for-each select="section">                      <xsl:if test="@title!='null'">                          <h2>                              <xsl:value-of select="@title" />                          </h2>                      </xsl:if>                      <xsl:for-each select="subsection">                          <xsl:if test="@title!='null'">                              <h3>                                  <xsl:value-of select="@title" />                              </h3>                          </xsl:if>                          <xsl:for-each select="body/p">                              <p>                                  <xsl:for-each select="text()">                                      <xsl:value-of select="current()" />                                  </xsl:for-each>                                  <xsl:for-each select="b">                                      <strong>                                          <xsl:value-of select="current()" />                                      </strong>                                  </xsl:for-each>                                  <xsl:for-each select="int">                                      <a href="#">                                          <xsl:value-of select="current()" />                                      </a>                                  </xsl:for-each>                                  <xsl:for-each select="ext">                                      <a href="#">                                          <xsl:value-of select="current()" />                                      </a>                                  </xsl:for-each>                              </p>                          </xsl:for-each>                      </xsl:for-each>                  </xsl:for-each>              </body>          </html>      </xsl:template>  </xsl:stylesheet>    

But the HTML output is out of order:

  <html>      <head>          <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">          <title>Chapter 1</title>          <style type="text/css">              body {              padding: 50px 10%;              }          </style>      </head>      <body>          <h1>Chapter 1</h1>          <h2>The Quick Brown Fox</h2>          <p>              Lorem ipsum , consectetur adipiscing elit.              Maecenas sed pretium nunc.              Proin tincidunt sapien dolor, posuere varius dui efficitur ac.              <strong>dolor sit amet</strong><a href="#">foo/bar/qux</a><a href="#">http://google.com/</a>          </p>          <p>              Foo bar , foo adipiscing elit.              Maecenas sed pretium nunc.              Proin tincidunt sapien dolor, posuere varius dui efficitur ac.              <strong>doqux amet</strong><a href="#">foo/bar/qux</a><a href="#">http://google.com/</a>          </p>      </body>  </html>    

No comments:

Post a Comment