Support #6521
closedNode not matching when not in "pretty print"
0%
Description
Match always found for ancestor::*[matches(@visibleWhen, '.*!=\s?null')][1] = current() when the source document matching elements are not separated by a text node.
This is the stripped down source xml:
<?xml version="1.0" encoding="UTF-8"?>
<cages><FileRoot name="Page1.xml"><content visibleWhen="level1 != null"><content visibleWhen="level2 != null"><content visibleWhen="level3 != null"><property name="standalone_prop"/></content></content></content></FileRoot></cages>
This is the xslt:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:template match="/">
<html>
<head>
<style>
table {
width: 100%;
border: 1px solid #ddd;
}
.subtable tr:nth-child(even) {
background-color: #eee;
}
.headerbg {
background-color: #ffff66;
}
.subtablehighlight tr:hover {background-color: #ffffe6;}
.column1 {
width: 80px;
}
fieldset {
margin-bottom: 30px;
}
</style>
</head>
<body>
<h1>Visible When</h1>
<!-- Select all files -->
<xsl:apply-templates select="//FileRoot"/>
</body>
</html>
</xsl:template>
<xsl:template match="FileRoot">
<!-- Main section per file -->
<fieldset>
<!-- File name -->
<legend><xsl:value-of select="./@name"/></legend>
<!-- Only grab parent visibleWhen elements; children get addressed within the parent -->
<xsl:apply-templates select="descendant::*[matches(@visibleWhen, '.*!=\s?null.*') and not(contains(string-join(ancestor::*/local-name(), ' '), ./local-name()))]"/>
</fieldset>
</xsl:template>
<xsl:template match="*[matches(@visibleWhen, '.*!=\s?null')]">
<!-- sub table of each content/page and its subsequent properties -->
<table class="subtable subtablehighlight" style="margin-bottom:10px" border="3">
<tr>
<!-- Header with element name and the visible when clause -->
<th class="column1 headerbg"><xsl:value-of select="local-name()"/></th>
<th style="text-align:left" class="headerbg"><xsl:value-of select="./@visibleWhen"/></th>
</tr>
<!-- Show properties, injections and labels only if they are under the current visibleWhen -->
<!-- <xsl:apply-templates select=".//property[generate-id(ancestor::*[matches(@visibleWhen, '.*!=\s?null')][1]) = generate-id(current())]"/> -->
<xsl:apply-templates select=".//property[ancestor::*[matches(@visibleWhen, '.*!=\s?null')][1] = current()]"/>
<!-- Pass current element to sub-template -->
<xsl:call-template name="ChildVisibleWhen">
<xsl:with-param name="parentNode" select="."/>
</xsl:call-template>
</table>
</xsl:template>
<xsl:template match="property">
<tr>
<td class="column1">Property:</td>
<td><xsl:value-of select="./@name"/></td>
</tr>
</xsl:template>
<xsl:template name="ChildVisibleWhen">
<xsl:param name="parentNode"/>
<tr>
<td/>
<!-- <td><xsl:apply-templates select="descendant::*[matches(@visibleWhen, '.*!=\s?null.*')][generate-id(ancestor::*[matches(@visibleWhen, '.*!=\s?null')][1]) = generate-id($parentNode)]"/></td> -->
<td><xsl:apply-templates select="descendant::*[matches(@visibleWhen, '.*!=\s?null.*')][ancestor::*[matches(@visibleWhen, '.*!=\s?null')][1] = $parentNode]"/></td>
</tr>
</xsl:template>
</xsl:stylesheet>
I expect this:
I get this:
I can add a space or newline between any <content>
and then it works fine. Since I can't guarantee the source will be formatted with a text node I've worked around it by using the generate-id function. From what I can tell I'm comparing single node sets so they should match regardless. I'm not seeing what is unique that comparing in these different scenarios should change the result.
line 62/63 and 83/84 are in here to make toggling between a working and not working template easier.
I've validated this happens in the saxon-he-12.5 w/xmlresolver-5.2.2.jar
Files
Updated by Martin Honnen 3 months ago
Perhaps you are looking for the is
operator that checks node identity:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:template match="/">
<html>
<head>
<style>
table {
width: 100%;
border: 1px solid #ddd;
}
.subtable tr:nth-child(even) {
background-color: #eee;
}
.headerbg {
background-color: #ffff66;
}
.subtablehighlight tr:hover {background-color: #ffffe6;}
.column1 {
width: 80px;
}
fieldset {
margin-bottom: 30px;
}
</style>
</head>
<body>
<h1>Visible When</h1>
<!-- Select all files -->
<xsl:apply-templates select="//FileRoot"/>
</body>
</html>
</xsl:template>
<xsl:template match="FileRoot">
<!-- Main section per file -->
<fieldset>
<!-- File name -->
<legend><xsl:value-of select="./@name"/></legend>
<!-- Only grab parent visibleWhen elements; children get addressed within the parent -->
<xsl:apply-templates select="descendant::*[matches(@visibleWhen, '.*!=\s?null.*') and not(contains(string-join(ancestor::*/local-name(), ' '), ./local-name()))]"/>
</fieldset>
</xsl:template>
<xsl:template match="*[matches(@visibleWhen, '.*!=\s?null')]">
<!-- sub table of each content/page and its subsequent properties -->
<table class="subtable subtablehighlight" style="margin-bottom:10px" border="3">
<tr>
<!-- Header with element name and the visible when clause -->
<th class="column1 headerbg"><xsl:value-of select="local-name()"/></th>
<th style="text-align:left" class="headerbg"><xsl:value-of select="./@visibleWhen"/></th>
</tr>
<!-- Show properties, injections and labels only if they are under the current visibleWhen -->
<!-- <xsl:apply-templates select=".//property[generate-id(ancestor::*[matches(@visibleWhen, '.*!=\s?null')][1]) = generate-id(current())]"/> -->
<xsl:apply-templates select=".//property[ancestor::*[matches(@visibleWhen, '.*!=\s?null')][1] is current()]"/>
<!-- Pass current element to sub-template -->
<xsl:call-template name="ChildVisibleWhen">
<xsl:with-param name="parentNode" select="."/>
</xsl:call-template>
</table>
</xsl:template>
<xsl:template match="property">
<tr>
<td class="column1">Property:</td>
<td><xsl:value-of select="./@name"/></td>
</tr>
</xsl:template>
<xsl:template name="ChildVisibleWhen">
<xsl:param name="parentNode"/>
<tr>
<td/>
<!-- <td><xsl:apply-templates select="descendant::*[matches(@visibleWhen, '.*!=\s?null.*')][generate-id(ancestor::*[matches(@visibleWhen, '.*!=\s?null')][1]) = generate-id($parentNode)]"/></td> -->
<td><xsl:apply-templates select="descendant::*[matches(@visibleWhen, '.*!=\s?null.*')][ancestor::*[matches(@visibleWhen, '.*!=\s?null')][1] is $parentNode]"/></td>
</tr>
</xsl:template>
</xsl:stylesheet>
Updated by Michael Kay 3 months ago
The expression
ancestor::*[matches(@visibleWhen, '.*!=\s?null')][1] = current()
is comparing the string value of the first ancestor node that matches the pattern to the string value of the current node. The string value of a node is the concatenation of its descendant text nodes, so whitespace text nodes are going to affect the outcome.
I haven't followed through your logic, but the fact that it works with a generate-id()
comparison does suggest that you used the "=" operator when you intended "is".
I'm wondering if there is a better way of writing
.//property[ancestor::*[matches(@visibleWhen, '.*!=\s?null')][1] is current()]
Perhaps something like
.//property except .//*[matches(@visibleWhen, '.*!=\s?null')]//property
though that's not obviously much of an improvement.
Updated by Justin Roegner 3 months ago
Thanks for the explanation. It makes sense now why it was bringing in the text node when doing that comparison. Both the "is" and "except" methods are logical resolutions to resolve what I thought was a bug. It was just an error on my part. Thanks again. I really appreciate it.
Updated by Michael Kay 3 months ago
- Tracker changed from Bug to Support
- Status changed from New to Closed
Please register to edit this issue