6

I can't figure out how to access all the attributes in a tag from an XML document.

Let's say I have the following XML:

<names>
  <name firstname="Rocky" lastname="Balboa" divider=", "/>
  <name firstname="Ivan" lastname="Drago" divider=", "/>
</names>

I want the following output: Rocky Balboa, Ivan Drago,

What I currently have is:

<xsl:for-each select="names/name">
   <xsl:value-of select="@firstname"/>
   <xsl:value-of select="@lastname"/>
   <xsl:value-of select="@divider"/>
</xsl:for-each>

What I'm wondering is if it's possible to do this in just one value-of select instead of having to do three of them. So to clarify, I want to be able to output all the attributes in the tag with one single value-of select. Is this possible?

Thanks.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
Erik Åstrand
  • 369
  • 2
  • 5
  • 14
  • You can use this XPath @* to get all attributes, e.g.:
    – user1188611 May 15 '13 at 15:53
  • please consider the fromatting. Due to strange reasons formatting is not working on my end. – user1188611 May 15 '13 at 15:54
  • Alright I guess you could do that aswell. But I want to figure out how/if this is possible to do it in the way I have, but with just one value-of select. – Erik Åstrand May 15 '13 at 15:56
  • That is what I mentioned. If you refer to above example @* use you will see it will require just one value-of select and you will be good. I hope this helps. I am re-posting my comment with better indentation and formatting below. Hoe that will help. – user1188611 May 15 '13 at 16:44
  • How would you like to handle blanks. This is not even reasonable in your current _solution_. (You are generation `RockyBalboa, IvanDrago, `.) – hr_117 May 15 '13 at 16:46
  • Why is it you have to use value-of. I think the solution would be to have a template for name (perhaps with a mode) and have than one line like: `` – hr_117 May 15 '13 at 17:00

5 Answers5

6

try the following:

<xsl:template match="/">
 <xsl:for-each select="names/name/@*">
        <xsl:value-of select="concat( ., ' ')"/>
  </xsl:for-each>
</xsl:template>     
james31rock
  • 2,615
  • 2
  • 20
  • 25
3

Because I'm not sure if the use of xsl:value-ofis a hard requirement, perhaps something like the following could be what you are locking for.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>

    <xsl:template match="name" mode ="print" >
        <xsl:value-of select="@firstname"/>
        <xsl:text> </xsl:text>
        <xsl:value-of select="@lastname"/>
        <xsl:value-of select="@divider"/>
    </xsl:template>

    <xsl:template match="/">
        <xsl:apply-templates  select="names/name" mode="print"/>
    </xsl:template>

</xsl:stylesheet>

You can use <xsl:apply-templates select="names/name" mode="print"/> at any position you have considered about using a one line value-of for all attributes.
The above template will generate the following output:

Rocky Balboa, Ivan Drago,

Update crate output without using the attribute names:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>

    <xsl:template match="name" mode ="print" >
        <xsl:for-each select="@*" >
            <xsl:if test="not(position() = last() or position() = 1)">
                <xsl:text> </xsl:text>
            </xsl:if>
            <xsl:value-of select="."/>

        </xsl:for-each>
    </xsl:template>

    <xsl:template match="/">
        <xsl:apply-templates  select="names/name" mode="print"/>
    </xsl:template>

</xsl:stylesheet>
hr_117
  • 9,589
  • 1
  • 18
  • 23
1

You can use this XPath @* to get all attributes, e.g.:

<xsl:template match="/*">
    <xsl:for-each select="@*">
        <xsl:value-of select="concat(name(), ': ', ., ' ')"/>
    </xsl:for-each>
</xsl:template>

This will let you use just one value-of select to get the output you want. It will take all attribute into consideration.

This should be a sufficient hint for you to figure out things. Let me know if you have any other question.

user1188611
  • 945
  • 2
  • 14
  • 38
  • This has not much to do with the request. You are generating something like `firstname: Ivan lastname:...` (if any). But requested is: `Rocky Balboa, Ivan Drago,` – hr_117 May 15 '13 at 16:52
0

If you can use XSLT 2.0, you can do something like this:

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

    <xsl:template match="text()"/>

    <xsl:template match="*[@*]">
        <xsl:value-of select="@*[not(name()='divider')]" separator=" "/>
        <xsl:value-of select="@divider"/>           
        <xsl:apply-templates/>
    </xsl:template>

</xsl:stylesheet>

This will output all attributes and you have no control over the order, so if you want to specify an order, you can either use a sequence:

<xsl:value-of select="(@firstname,@lastname)" separator=" "/>

or do an xsl:apply-templates with an xsl:sort to sort the attributes by name() (or whatever). Let me know if you'd like an example.

Daniel Haley
  • 51,389
  • 6
  • 69
  • 95
  • I must have explained myself wrong. I'm wondering if its possible to print out all the attributes, not but calling the names of the attributes. Something like value-of select "*" or along those lines. – Erik Åstrand May 15 '13 at 17:49
  • @ErikÅstrand - That's what `` does except that it ignores the `divider` attribute. You could also do ``, but you wouldn't be guaranteed that `divider` would be the last output. – Daniel Haley May 15 '13 at 18:55
0

The following works in XSLT 2.0:

<xsl:for-each select="names/name">
   <xsl:value-of select="@firstname, @lastname, @divider"/>
</xsl:for-each>

and in 3.0 you can do:

<xsl:value-of select="names/name!(@firstname, @lastname, @divider)"/>

though you may need to make adjustments to get the whitespace the way you want it.

Michael Kay
  • 156,231
  • 11
  • 92
  • 164
  • Hey. Thanks for the reply. I'm not acually wondering how to print out all the attributes in one line by calling the acual names of the attributes. I'm looking for a way to simply call all the attributes, regardless of the names. Something along these lines: value-of select = " * ". Something that can print all the attributes, but not by calling them individually. – Erik Åstrand May 15 '13 at 17:51