5

I want to write an XSL template that matches attributes instead of nodes,

I would think that having something like this:

<xsl:template match="@href | @conref | @conrefend">
    <xsl:message select="."/>
</xsl:template>

would match any of those 3 attribute names and print to console the value of the attribute as the scope is the attribute itself and not a node.

But my testing has proven me wrong and I'm only able to match nodes that contain any of those attributes like this:

 <xsl:template match="*[@href or @conref or @conrefend]">
   <xsl:message select="if(not(@href)) 
                        then    
                          if(not(@conref)) 
                          then @conrefend 
                          else @conref 
                        else @href"/>  
   </xsl:template>

The problem with that approach is that if there happens to exist a node that has more than one of those attributes then only one gets processed and I need to process them all.

Any ideas of why the first approach doesn't work?

EDIT1: Full xslt:

  <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      xmlns:xs="http://www.w3.org/2001/XMLSchema"
      exclude-result-prefixes="xs"
      version="2.0">

     <xsl:output omit-xml-declaration="yes" indent="yes"/>
     <xsl:strip-space elements="*"/>             
     <xsl:template match="@conref|@conrefend|@href">
        <xsl:message select="."/>
     </xsl:template>
   </xsl:stylesheet>

Test XML:

  <links>
      <image conref="COPY-GUID/*+-862416}39-37CD-4CF7-A7AA-F09F4A763944" />
   </links>

Right now the XSLT is not matching anything.

ROMANIA_engineer
  • 54,432
  • 29
  • 203
  • 199
rovinos
  • 222
  • 1
  • 3
  • 7

3 Answers3

3

You can indeed match attributes in your templates as your first template does. Something else must be wrong...

Take a look at the identity transform:

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

It works fine matching alternatives as you're doing, and one of the alternatives is for all attributes (@*).

Now consider the xsl:apply-templates statement. Notice how @* is called out explicitly. Are you sure that you're giving your template a chance to apply to attributes? If you're calling, xsl:apply-templates like this, for example:

        <xsl:apply-templates select="*"/>

realize that only elements will be selected, not attributes. Note also that node() does not include attributes either.

Update

Right. Your attribute-matching template never has a chance to apply.

Add the identity template above, or something like this:

<xsl:template match="/">
    <xsl:apply-templates select="//@*"/>
</xsl:template>

to give your attribute-matching template a chance to apply.

kjhughes
  • 106,133
  • 27
  • 181
  • 240
  • I think I don't fully understand how the xslt matches work, I'll update my question with the full xslt and entry xml to see if there is something wrong with that – rovinos Feb 03 '15 at 17:05
1

By default, XSLT does not look for templates matching attribute nodes. Explicitly apply templates to all attributes in the input XML.

By the way, are you sure that you want to use xsl:message? The content of an xsl:message is not included in the transformation output. Do you mean xsl:value-of? Also, if you output text, you should declare the output method to be text.

Stylesheet

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

    <xsl:output method="text"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="/">
        <xsl:apply-templates select="//@*"/>
    </xsl:template>

    <xsl:template match="@conref">
        <xsl:value-of select="."/>
    </xsl:template>

</xsl:stylesheet>

Text Output

COPY-GUID/*+-862416}39-37CD-4CF7-A7AA-F09F4A763944

Actually, in this very simple case, the first template would suffice to get the same output:

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

    <xsl:output method="text"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="/">
        <xsl:apply-templates select="//@*"/>
    </xsl:template>

</xsl:stylesheet>

This is because, once templates are being applied to attribute nodes, there is a built-in template that outputs their string value by default.

Mathias Müller
  • 22,203
  • 13
  • 58
  • 75
  • Mathias can you help me with this question https://stackoverflow.com/questions/67897747/how-to-get-following-sibling-from-xml-using-xslt-from-child-node – lazydevpro Jun 09 '21 at 05:53
1

The problem with your current approach is that the template matching the attribute/s is never applied. It isn't applied, because the built-in template rule matches only the root node and element nodes, and it applies templates only to children nodes. To apply a template to attributes, you must apply it explicitly yourself.

http://www.w3.org/TR/xslt20/#built-in-rule

michael.hor257k
  • 113,275
  • 6
  • 33
  • 51