2

The context is a XSLT identity transformation with

<xsl:template match="abstract[@xml:lang]">
    <xsl:copy>
       <xsl:apply-templates select="@*[not(self::xml:lang)]|node()"/>
    </xsl:copy>
</xsl:template>

so, I expected to remove the attribute xml:lang.

kjhughes
  • 106,133
  • 27
  • 181
  • 240
Peter Krauss
  • 13,174
  • 24
  • 167
  • 304
  • 1
    So what? What happened then? Can you give a [little more context please](http://stackoverflow.com/help/mcve)? A sample input XML, a complete stylesheet, the output and explaining what's wrong with it would be a good start. – Mathias Müller Mar 04 '15 at 20:59
  • @MathiasMüller, I agree, but the answers was so fast and the "openess" was good for 3 excelent and different answers. See my comments at JLRishe's, the best explanation for context. – Peter Krauss Mar 09 '15 at 12:24

3 Answers3

2

You should have seen a warning along the lines of the following:

Warning! The self axis will never select any element nodes when starting at an attribute node

You could instead test the attribute's name():

  <xsl:template match="abstract[@xml:lang]">
    <xsl:copy>
      <xsl:apply-templates select="@*[name() != 'xml:lang']|node()"/>
    </xsl:copy>
  </xsl:template>

This will effectively delete the xml:lang attribute from abstract as requested.

kjhughes
  • 106,133
  • 27
  • 181
  • 240
  • Where would OP have seen that warning? – JLRishe Mar 08 '15 at 17:40
  • @JLRishe: That particular warning is emitted by SaxonHE9-5-1-2J for OP's template. – kjhughes Mar 08 '15 at 18:17
  • Ah. I don't see any mention of OP using Saxon, but perhaps OP has mentioned using it in the past? – JLRishe Mar 08 '15 at 18:20
  • @JLRishe: Sorry if my brevity misled. Merely meant that **ideally** OP *should have seen a warning along the lines of the following* (message provided by Saxon). – kjhughes Mar 08 '15 at 18:30
  • I see. I don't know if most XSLT processors provide warnings for potentially useless XPaths. .NET's XSLT processor sure doesn't, but thank you for clarifying nonetheless. – JLRishe Mar 08 '15 at 18:52
  • @JLRishe: Using [Saxon](http://www.saxonica.com/products/products.xml) is like including a little piece of Michael Kay in one's XSLT edit-run-debug cycle. Recommended. ;-) – kjhughes Mar 08 '15 at 19:14
  • Thanks! Was difficult to click on "accept" here, see my comment at @JLRishe's answer. – Peter Krauss Mar 09 '15 at 12:19
2

There will never be an attribute on the self axis (unless you're using the libxslt processor...).
see discussion in comments bellow

Why don't you do simply:

<xsl:template match="abstract/@xml:lang"/>

to suppress the unwanted attribute specifically?


Working demo: http://xsltransform.net/eiZQaFp

michael.hor257k
  • 113,275
  • 6
  • 33
  • 51
  • make sense, but this technic only works for elements, not for attribute nodes. Test by your self... – Peter Krauss Mar 05 '15 at 00:26
  • 1
    An attribute can be on the self axis. `self::node()` applied to an attribute returns the attribute (but `self::*` doesn't). – nwellnhof Mar 08 '15 at 14:52
  • @PeterKrauss I test my answers **before** I post them. I have posted this comment before, along with a link to a demo that shows my answer working. Unfortunately there are people here that see fit to remove my comments without rhyme or reason. – michael.hor257k Mar 08 '15 at 16:24
  • @nwellnhof "*node() matches any node other than an attribute node and the root node*" http://www.w3.org/TR/xslt/#patterns – michael.hor257k Mar 08 '15 at 16:25
  • 1
    That's because `node()` is equivalent to `child::node()`, not `self::node()`. – nwellnhof Mar 08 '15 at 16:33
  • @michael.hor257k The text you quoted is referring to the case where `node()` is the entire pattern (and it's in a section about pattern matching, not about XPath in general). `self::node()` is something else. – JLRishe Mar 08 '15 at 16:41
  • @nwellnhof I am afraid I don't see what difference it makes. – michael.hor257k Mar 08 '15 at 16:43
  • @JLRishe If `child::node()` is not supposed to include attributes, but `self::node()` is, then something's wrong with this world. – michael.hor257k Mar 08 '15 at 16:50
  • @michael.hor257k Why? There was a design decision at some point that attributes are not "children" and therefore are never on the `child::` axis. Perhaps you can take issue with `child::node()` not matching attributes, but I see nothing wrong with attributes being on the `self::` axis w.r.t. themselves. – JLRishe Mar 08 '15 at 16:52
  • @michael.hor257k Most notably, the XPath spec contains this text _"A location step of . is short for self::node()."_ Surely, you would agree that `.` can refer to an attribute? Also _"the self axis contains just the context node itself"_ and _"A node test node() is true for any node of any type whatsoever."_ It's quite clear from all of this that both `self::` and `node()` can refer to attributes. I have upvoted your answer because I think it's the best solution in this case, but please correct that first sentence. It is incorrect. – JLRishe Mar 08 '15 at 16:59
  • @JLRishe When I wrote my reply, I was relying on the following quote from Jeni Tennison: "*It's only with attributes and namespace nodes that **you can't use the self:: axis because they're only accessible through the attribute:: and namespace:: axes.***" Now I see that on the same page*, the same Jeni Tennison says: "*You can tell that the node() node test matches attributes because the expression ".", which expands to the expression "self::node()", can be used to select attributes. Unlike the child axis, the self axis selects the context node no matter what its type.*" ... – michael.hor257k Mar 08 '15 at 17:08
  • .... IMHO, expressions like node(), text(), etc. should not change their meaning when placed on a different axis - but I didn't write the specs. --- (*) http://www.dpawson.co.uk/xsl/sect2/nodetest.html – michael.hor257k Mar 08 '15 at 17:08
  • @michael.hor257k `node()` and `text()` don't change their meaning. The problem is that when an axis is unspecified, the default axis is `child::` and attributes are not on the child axis. As I've already said, if anything, your beef is with the confusing (but still well-defined) behavior of the `child::` axis. Luckily, the XPath spec is not subject to your opinion, so could you correct your answer (pretty please?). – JLRishe Mar 08 '15 at 17:14
  • 1
    Sorry @michael.hor257k, I mistook, with [this other case, for replace attribute value](http://stackoverflow.com/q/24899992/287948). Your solution is correct for XSLTv1 (!). It is a valid solution when the problem is really only to remove attribute. – Peter Krauss Mar 08 '15 at 17:40
  • @PeterKrauss I believe my solution is equally valid for XSLT 1.0 and XSLT 2.0. Is there a reason why you are not accepting my answer (or any answer), here and in the other thread? – michael.hor257k Mar 08 '15 at 17:48
2

The issue here is that self::[QName] can only refer to an element, not an attribute1.

I think michael.hor257k's suggestion is the best for this particular case, and checking the name as in kjhughes' answer should be ok in this particular case because it is about the xml namespace, which is unambiguous.

But in general, if you wanted to exclude a single attribute without relying on name(), you can do this:

<xsl:template match="abstract[@xml:lang]">
    <xsl:copy>
       <xsl:apply-templates select="@*[(. | ../@xml:lang)[2]]|node()"/>
    </xsl:copy>
</xsl:template>

http://xsltransform.net/eiZQaFp/2

This will only work correctly if the xml:lang attribute is present, but your pattern in the match attribute here ensures that it is indeed present.


  1. Here is the relevant text from the XPath spec that explains why this is true:

Every axis has a principal node type. If an axis can contain elements, then the principal node type is element; otherwise, it is the type of the nodes that the axis can contain. Thus,

  • For the attribute axis, the principal node type is attribute.
  • For the namespace axis, the principal node type is namespace.
  • For other axes, the principal node type is element.

A node test that is a QName is true if and only if the type of the node (see [5 Data Model]) is the principal node type and has an expanded-name equal to the expanded-name specified by the QName. For example, child::para selects the para element children of the context node; if the context node has no para children, it will select an empty set of nodes. attribute::href selects the href attribute of the context node; if the context node has no href attribute, it will select an empty set of nodes.

The principal node type of self:: is element, so if it is followed by a QName (such as xml:lang), then self::xml:lang can only refer to an element, not an attribute.

Community
  • 1
  • 1
JLRishe
  • 99,490
  • 19
  • 131
  • 169
  • Thanks, you explained the point of the question (!). By other hand, the simplest solution and explanaition was @kjhughes's: in my opinion `@*[(. | ../@xml:lang)[2]]|node()` is somewhat "encryptic" when comparated with (more friendly syntax) `@*[name() != 'xml:lang']|node()`. So you show the best explanation (with the good and relevant [w3.org/TR/xpath v1](http://www.w3.org/TR/xpath/) citation!), kjhughes's the first and complete solution, and michael.hor257k the simplest alternative (not good for me but very good for other readers). – Peter Krauss Mar 09 '15 at 12:16