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"/>