200_success
200_success

Reputation: 7582

XPath expression /node()[1] selects more than the first node?

I have a document doc.xml:

<?xml version="1.0"?>
<!-- This is the first top-level node -->
<!-- This is the second top-level node -->
<ThirdNode/>
<!-- This is the fourth top-level node -->

I'd like to select the first top-level node from the document, in firstnode.xsl:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/node()[1]">First</xsl:template>
    <xsl:template match="@*|node()">
        <xsl:copy><xsl:apply-templates select="@*|node()"/></xsl:copy>
    </xsl:template>
</xsl:stylesheet>

When I run xsltproc firstnode.xsl doc.xml, I get:

<?xml version="1.0"?>
FirstFirst<ThirdNode/><!-- This is the fourth top-level node -->

… instead of the expected output:

<?xml version="1.0"?>
First
<!-- This is the second top-level node -->
<ThirdNode/>
<!-- This is the fourth top-level node -->

Why does the /node()[1] selector match more than just the first comment? How can I select the first node of the document (regardless of whether it is a comment, element, or whatever)?

Upvotes: 0

Views: 334

Answers (2)

michael.hor257k
michael.hor257k

Reputation: 117165

I have managed to reproduce your problem using the libxlt processor, but not with Xalan or Saxon - which leads me to conclude that this is non-conforming behavior.

Changing the match pattern to:

<xsl:template match="node()[not(parent::*)][1]">

seems to bypass the issue.


Surprisingly, even adding a bogus condition to your original expression, such as:

<xsl:template match="/node()[not(0)][1]">

or even:

<xsl:template match="/node()[1][1]">

also works, so this is clearly a bug.

Upvotes: 3

zx485
zx485

Reputation: 29052

I am not sure if I did correctly understand your question. However, I tried to reproduce your question and the result was this:

Input.xml:

<?xml version="1.0"?>
<First>
  <Second>
    <ThirdNode/>      
    <Fourth />
  </Second>
</First>

Processing.xslt:

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

    <xsl:template match="/node()[1]">First1
      <xsl:apply-templates select="node()|@*" />
    </xsl:template>

    <xsl:template match="@*|node()">
        <xsl:copy><xsl:apply-templates select="@*|node()"/></xsl:copy>
    </xsl:template>

</xsl:stylesheet>

Output:

<?xml version="1.0"?>
First1

  <Second>
    <ThirdNode/>      
    <Fourth/>
  </Second>

Maybe this helps you in some way.

Upvotes: 0

Related Questions