XPath 1.0 Order of returned attributes in a UNION

2020-07-11 08:27发布

<merge>
    <text>
        <div begin="A"   end="B" />
        <div begin="C"   end="D" />
        <div begin="E"   end="F" />
        <div begin="G"   end="H" />
    </text>
</merge>

I need a UNIONed set of attribute nodes, in the order A,B,C,D,E,F,G,H, and this will work:

/merge/text/div/@begin | /merge/text/div/@end

but only if each @begin comes before each @end, since the UNION operator is spec'd to return nodes in document order. (Yes?)

I need the nodeset to be in the same order, even if the attributes appear in a different order in the document, as here:

<merge>
    <text>
        <div end="B"   begin="A" />
        <div begin="C" end="D"   />
        <div end="F"   begin="E" />
        <div begin="G" end="H"   />
    </text>
</merge>

That is, I need elements to follow document order, but the attributes in each element to follow a determined order (either specified or alphabetical by attribute name).

标签: xpath
1条回答
淡お忘
2楼-- · 2020-07-11 09:10

This simply isn't possible in pure XPath. First of all, attributes in XML are unordered. From the XML 1.0 Recommendation:

Note that the order of attribute specifications in a start-tag or empty-element tag is not significant.

An XPath engine might be reading and storing them in the order they appear in the document, but in terms of the spec, this is just a happy coincidence that cannot be relied upon.

Second, XPath has no sorting functionality. So, your best option is to sort the elements in your host language (e.g. XSLT or a general-purpose PL) after they've been selected.

Here's how to sort those attributes by value in XSLT:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text"/>
    <xsl:template match="/">
        <xsl:apply-templates
            select="/merge/text/div/@*[name()='begin' or name()='end']">
            <xsl:sort select="."/>
        </xsl:apply-templates>
    </xsl:template>
</xsl:stylesheet>

Note that I also merged your two expressions into one.

Edit: Use the following to output begin/end pairs in document order (as described in the comments):

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text"/>
    <xsl:strip-space elements="*"/>
    <xsl:template match="div">
        <xsl:value-of select="concat(@begin, @end)"/>
    </xsl:template>
</xsl:stylesheet>
查看更多
登录 后发表回答