3

I'd like to parse the HTML inside of a CDATA block for <p> tags and output each in a separate table row. However I can't quite figure it out and was wondering if someone would be able to help me out?

I've been trying to parse the HTML but am unable to figure out how I can parse it and not simply regard it as character data. I'm pretty sure I'm unable to do this with XSL 1.0, I'm able to use 2.0 if needed.

XML

<XML_FILE>
  <NOTE>
    <TEXT TITLE="TEST">
      <![CDATA[<p>first p tag and <strong>bold</strong></p><p>second p tag and  <u>underline</u></p>]]>
    </TEXT>
  </NOTE>
</XML_FILE>

XSL

<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

  <xsl:template match="NOTE">
    <div class="tableWrapper">
      <table class="body">
        <xsl:apply-templates select="TEXT"/>
      </table>
    </div>
  </xsl:template>

  <xsl:template match="TEXT">
    <xsl:value-of select="." disable-output-escaping="yes"/>
  </xsl:template>

</xsl:stylesheet>

Output

<div class="tableWrapper">
   <table class="body"><p>first p tag and <strong>bold</strong></p><p>second p tag and <u>underline</u></p></table>
</div>

Desired Output

<div class="tableWrapper">
   <table class="body">
      <tr><td><p>first p tag and <strong>bold</strong></p></td></tr>
      <tr><td><p>second p tag and <u>underline</u></p></td></tr>
   </table>
</div>

Final stylesheet that provides desired output

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="#all"
    version="3.0">

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:output method="html" indent="yes" html-version="5"/>
  <xsl:strip-space elements="*"/>

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

  <xsl:template match="NOTE">
    <div class="tableWrapper">
      <table class="body">
        <xsl:apply-templates select="parse-xml-fragment(TEXT)/node()"/>
      </table>
    </div>
  </xsl:template>

  <xsl:template match="p">
      <tr>
          <td>
              <xsl:next-match/>
          </td>
      </tr>
  </xsl:template>

</xsl:stylesheet>

1 Answer 1

2

XSLT 3.0 has a function parse-xml-fragment() which will tackle this.

There's nothing equivalent in earlier XSLT versions, though you might find vendor extensions that help you out. Most processor allow you to write your own external functions that you can invoke from your XSLT code, and you could write such a function that passed the CDATA content to an external XML parser for conversion into a tree structure.

Sign up to request clarification or add additional context in comments.

4 Comments

parse-xml-fragment works to get the content of the <p> tags, but how can I iterate through the <p> nodes? Using <xsl:value-of select="parse-xml-fragment(.)" disable-output-escaping="yes"/> gives the output <table class="body">first p tag and boldsecond p tag and underline</table>
parse-xml-fragment(.) returns the document-node(). So you should generate tr and td in match="TEXT" template first and adding <xsl:copy-of select="parse-xml-fragment(.)"/> inside td will work.
This is closer, however still not quite what I'm looking for. Using <tr><td><xsl:copy-of select="parse-xml-fragment(.)"/></td></tr> gives the output <tr><td><p>first ...</p><p>second ...</p></td></tr>. The output I'm hoping for is <tr><td><p>first...</p></td></tr><tr><td><p>second...</p></td></tr>. Perhaps I misunderstood?
Of course if you want to map/transform the nodes returned by parse-xml-fragment (e.g. the p elements to be wrapped into tr and td ones) you can do that with further templates, see xsltfiddle.liberty-development.net/ej9EGbT/1, like you would do with any nodes you need to transform, whether they are in a primary XML document or parsed by the doc function or in your case by parse-xml-fragment,

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.