Home > OS >  XSL - An attribute node (class) cannot be created after a child of the containing element
XSL - An attribute node (class) cannot be created after a child of the containing element

Time:10-13

I have the following xml to parse some html:

 <?xml version="1.0" encoding="utf-8"?>
  <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   xmlns="http://www.w3.org/1999/xhtml"
   version="2.0"
   xmlns:xs="http://www.w3.org/2001/XMLSchema"
   exclude-result-prefixes="#all"
   xmlns:d="data:,dpc"
   expand-text="yes">

  <xsl:import href="https://github.com/davidcarlisle/web-xslt/raw/main/htmlparse/htmlparse.xsl"/>

  <xsl:output method="html" html-version="5.0"/>

  <xsl:mode name="html"/>

  <xsl:template mode="html" match="b | strong | i | ul | ol | li | p | br | p | div | p/@style | div/@class" xpath-default-namespace="http://www.w3.org/1999/xhtml">
     <xsl:copy>
        <xsl:apply-templates select="@* | node()" mode="#current"/>
     </xsl:copy>
   </xsl:template>

  <xsl:template mode="html" match="script" xpath-default-namespace="http://www.w3.org/1999/xhtml"/>

  <xsl:template match="/">
    <html>
       <head>
         <title>Test</title>
       </head>
       <body>
          <xsl:apply-templates/>
       </body>
    </html>
  </xsl:template>

  <xsl:template match="orders">
    <h1>Orders</h1>
     <xsl:where-populated>
       <ol>
         <xsl:apply-templates/>
       </ol>
     </xsl:where-populated>
  </xsl:template>

  <xsl:template match="order">
     <li>Order
       <div>
         <h2>Comments</h2>
       <div>
         <xsl:apply-templates select="d:htmlparse(comments)" mode="html"/>
       </div>
      </div></li>
  </xsl:template>

</xsl:stylesheet>

If I try to parse the following html:

<comments>
  <![CDATA[<div id="123" >
    Some comment
  </div>]]>
</comments>

I get the following error:

Error executing XSLT at line 17 : An attribute node (class) cannot be created after a child of the containing element.

I take this is because I've defined the matching as div/@class and in the html I've got an id attribute before it.

How can I define the match so that the class attribute can appear in the html div element in any order regardless of what other attributes are defined?

CodePudding user response:

When xsl:apply-templates processes the attribute div/@id, it chooses the default template, which produces a text node with the attribute value. At this point, the result tree looks so:

<div>123</div>

and when the attribute div/@class is processed next, it is too late for the attribute to be copied (as your first template wants to do).

The solution depends on what you want from the div/@id attribute or other attributes not covered by your first template. If you want to omit them, add a fallback template for other attributes:

<xsl:template match="@*" mode="html" priority="-1"
  xpath-default-namespace="http://www.w3.org/1999/xhtml"/>
  • Related