SitePoint Sponsor

User Tag List

Results 1 to 9 of 9
  1. #1
    SitePoint Zealot
    Join Date
    Dec 2001
    Location
    UK
    Posts
    105
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    XSLT: can't get {local-name()} to work

    Okay, I've just started with XSLT this weekend. I bought a book and read some articles and am just starting to really get into it.

    I have a question. Purely for the purpose of an exercise I wish to iterate through an XML document and convert some child elements to attributes for a parent element. I know it's generally bad-practice to go overboard with attributes but I am doing this purely as an exercise, not for any real-life purpose.

    As an example I would like this...
    Code:
    <name>
        <first>Fred</first>
        <middle>J.</first>
        <last>Bloggs</last>
    </name>
    ...to become...
    Code:
    <name first="Fred" middle="J." last="Bloggs" />
    Here's the XML document
    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <people>
        <name>
            <first>John</first>
            <middle>Fitzgerald Johansen</middle>
            <last>Doe</last>
        </name>
        <name>
            <first>Franklin</first>
            <middle>D.</middle>
            <last>Roosevelt</last>
        </name>
        <name>
            <first>Alfred</first>
            <middle>E.</middle>
            <last>Neuman</last>
        </name>
        <name>
            <first>John</first>
            <middle>Q.</middle>
            <last>Public</last>
        </name>
        <name>
            <first>Jane</first>
            <middle/>
            <last>Doe</last>
        </name>
    </people>
    and here's the Stylesheet
    Code:
    <?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" method="xml" encoding="UTF-8" />
    
    <!-- root document template -->
    <xsl:template match="/">
        <people>
            <!-- execute any templates defined for elements which are a child of <people> -->
            <xsl:apply-templates />
        </people>
    </xsl:template>
    
    <!-- match any <name> elements -->
    <xsl:template match="name">
        <!-- create an element called name and use the attribute set called attribs -->
        <xsl:element name="name" use-attribute-sets="attribs" />
    </xsl:template>
    
    <xsl:attribute-set name="attribs">
      <xsl:attribute name="{local-name()}"><xsl:value-of select="." /></xsl:attribute>
    
       <!-- THIS BIT WORKS WHEN UNCOMMENTED! 
       create three attributes, assign the value of the <first>, <middle>, or <last> elements to the attribute 
       <xsl:attribute name="first"><xsl:value-of select="first" /></xsl:attribute>
       <xsl:attribute name="middle"><xsl:value-of select="middle" /></xsl:attribute>
       <xsl:attribute name="last"><xsl:value-of select="last" /></xsl:attribute> 
       -->
    </xsl:attribute-set>
    
    </xsl:stylesheet>
    I can get it to work when I explicitly refer to the element names in the attribute-set, but not if I wish to be a bit more dynamic and use the local-name() function. Can someone please explain where I am going wrong here?

    Many Thanks

  2. #2
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Pheww.....

    20 Odd minutes later I find a solution, so here goes....

    XML File:

    PHP Code:
    <?xml version="1.0" encoding="utf-8"?>
    <root>
     <name>
      <first>Les</first>
      <middle>T</middle>
      <last>Quinn</last>
     </name>
     <name>
      <first>L</first>
      <middle>Thomas</middle>
      <last>Quinn</last>
     </name>
    </root>
    And next the stylesheet you need;

    PHP Code:
    <?xml version='1.0' encoding="utf-8"?>
    <xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0'>
    <xsl:output method='xml' encoding="utf-8" version='1.0' indent='yes' />
    <xsl:template match='root'>
     <xsl:apply-templates />
    </xsl:template>
    <xsl:template match='name'>
     <name>
      <xsl:attribute name='first'><xsl:value-of select='first' /></xsl:attribute>
      <xsl:attribute name='middle'><xsl:value-of select='middle' /></xsl:attribute>
      <xsl:attribute name='last'><xsl:value-of select='last' /></xsl:attribute>
     </name>
    </xsl:template>
    </xsl:stylesheet>
    And the output I get (via my classes and Sablotron - I had to select 'View Source' to see the output btw ? Don't really know why since the output method is XML)

    PHP Code:
    <?xml version="1.0" encoding="utf-8"?>
     <name first="Les" middle="T" last="Quinn"/>
     <name first="L" middle="Thomas" last="Quinn"/>
    This will sort out your exercise for you ? Hope it helps anyways....

  3. #3
    SitePoint Zealot
    Join Date
    Dec 2001
    Location
    UK
    Posts
    105
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks Doc.

    Problem is that I had that stylesheet already (look at the commented out section in the stylesheet in my first post)

    I was looking for a way of looping through the child elements, without having to know their names and dynamically assigning them to attributes. Does that make sense?

    I guess it could be done with a for-each loop but was just trying to do it using apply-templates and XPATH only. It's not important as, like I said, it's not for any real purpose. Just wondered how it could be achieved.

    Thanks anyway.

  4. #4
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I think I do ? You want to do what I have above but without having to match = 'name' correct ?

    If so, there is proberly a way to do this but can't think of one at the moment myself...

    When I run into trouble with XSL-T I usually go off to www.xml.com and have a look over their articles; another interesting website it www.topxml.com which covers complete tutorials; looking through their stylesheets shows up methods of doing something or other if you can spare the time to look over them properly ?

    I might suggest that you also look at www.ibm.com ? They've got some stuff in PDF format...

    Hope this helps ?

  5. #5
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Have you got this book ? I am planning on buying a copy very soon...

    http://www.xml.com/cookbooks/xsltckb...tion.csp?day=1

    Looks like it could provide a lot of useful information I think...

  6. #6
    SitePoint Zealot
    Join Date
    Dec 2001
    Location
    UK
    Posts
    105
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Dr Livingston
    Have you got this book ? I am planning on buying a copy very soon...

    http://www.xml.com/cookbooks/xsltckb...tion.csp?day=1

    Looks like it could provide a lot of useful information I think...
    Not yet, ask me again in a few days

    Thanks for the links, I'll take a look at those.

  7. #7
    SitePoint Zealot
    Join Date
    Dec 2001
    Location
    UK
    Posts
    105
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Just bought Oreilly's XSLT book (not the XSLT Cookbook, maybe later) and the XML Schema one at the same time
    Plenty of bedtime reading

  8. #8
    SitePoint Zealot
    Join Date
    Dec 2001
    Location
    UK
    Posts
    105
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Okay I got it working.
    Also, I found an excellent site for XSLT! Take a look

    Here's the working version:
    Code:
    <?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" method="xml" encoding="UTF-8" />
    
    <!-- root document template -->
    <xsl:template match="/">
        <people>
            <!-- execute any templates defined for elements which are a child of <people> -->
            <xsl:apply-templates />
        </people>
    </xsl:template>
    
    <!-- match any <name> elements -->
    <xsl:template match="name">
        <!-- create an element called name -->
        <xsl:element name="name">
            <!-- process each child element of the context node (name) -->
            <xsl:for-each select="child::*">
                <!-- set the attribute name to the name of the child element being processed -->
                <xsl:attribute name="{local-name()}">
                    <xsl:choose>
                        <!-- if element is NOT empty then assign its value to the newly created attribute -->
                        <xsl:when test="normalize-space(.)"><xsl:value-of select="." /></xsl:when>
                        <!-- if it is empty, then assign N/A to the attribute -->
                        <xsl:otherwise>N/A</xsl:otherwise>
    		   </xsl:choose>
                </xsl:attribute>
            </xsl:for-each>
        </xsl:element>    
    </xsl:template>
    </xsl:stylesheet>

  9. #9
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Glad you have it working - looking over your stylesheet in a minute

    Excellent links by the way; bookmarked this page...



Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •