Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations John Tel on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

using XSLT for tags based on attribute values 1

Status
Not open for further replies.

wgcs

Programmer
Mar 31, 2002
2,056
EC
I'm trying to create an XSLT to "denormalize" an XML document.

Basically, the starting XML has a "collection" tag with many "item" tags inside it. Each item tag has a different value for its "id" attribute.

I want to use XSLT to change that into one "collection" tag with many tags inside it, where each of those tags has a name based on the "id" attributes of the "item" tags in the first document.

for example, starting with this:
Code:
<collection>
  <item id="field1">value</item>
  <item id="field2">value</item>
  <item id="field3">value</item>
  <item id="field4">value</item>
</collection>

I need to XSLT it to:
Code:
<collection>
  <field1>value</field1>
  <field2>value</field2>
  <field3>value</field3>
  <field4>value</field4>
</collection>

I expect to use an xsl:for-each construct,
But I can't figure out how to get the result of
Code:
<xsl:value-of select="@id"/>
to supply the actual tag name.
 
Oh: I think I just figured out that "& lt;" (w/o the space) does the trick.
 
Ok, I tried it: the "& lt;" doesn't get converted by msxml2.DOMDocument's transformNode (as in lcfinal = [<?xml version="1.0"?>] + loMsXml.transformNode( loMSXSLT ) )

but it's easy enough to:
Code:
lcFinal = STRTRAN(lcFInal,'&lt;','<')
lcFinal = STRTRAN(lcFInal,'&gt;','>')

before using lcFinal.

If anyone knows of a way to eliminate the STRTRAN calls, do tell!
 
You can use the following xslt.

<xsl:stylesheet version="1.0" xmlns:xsl=" <xsl:eek:utput method="html"/>
<xsl:template match="/">
<xsl:apply-templates select="*"/>
</xsl:template>
<xsl:template match="collection">
<xsl:element name="collection">
<xsl:apply-templates select="*"/>
</xsl:element>
</xsl:template>
<xsl:template match="item">
<xsl:element name="{@id}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
 
Wow! that works! I don't yet understand how, but I'll see if I can fit it in... (my real document is more complex than the example I posted)

- Bill

Get the best answers to your questions -- See FAQ481-4875.
 
<xsl:element> and <xsl:attribute> are used to build xml elements and attributes just as if you had typed the following (for ex)

<a href="tektips.html">tektips</a>

<xsl:element name="a">
<xsl:attribute name="href">tektips.html</xsl:attribute>
tektips
</xsl:element>

So what I did above is essentially what should be used a looping mechanism. Avoid abuse of for-each, and use multiple small templates to match on nodes as move through the XML tree. So hit a element, then for every subelement, create a template and hit that element. Continue on by moving down from the element to element through apply-templates. <xsl:apply-templates select="*"/> just says find a template that matches every child.

So I start at the root: <xsl:template match="/">

Then I apply a <xsl:apply-templates select="*"/> to match every child element. The xslt parser finds a template that matches <collection> because it is the first child of the root. So I create a new collection element, just as if I had gone <collection>. Then I apply a <xsl:apply-templates select="*"/> to match every child element of the <collection> element. The next child is <item>, so every time it hits an <item> element, it looks for a template that matches that element. It finds it and beings to create your new element. In the item template, <xsl:template match="item"> the item element is our context node, so we can ask for its attribute value and use it as the name of the element <xsl:element name="{@id}">. Then we use the <item> element's text value as the text value for our new element <xsl:value-of select="."/>.

That's it.
 
Thanks! That certainly helps explain better.

I was wondering, as I follow the logic, when you say:
So I start at the root: <xsl:template match="/">

and then, in the template that applies (which is the immediately following template... this link, of having xsl:apply-template always refer to the "next" template, is what was confusing me the most... in reading more documentation, I've learned you can also use a "name" attribute to use any other template, not just the first following one. I mention this, just in case it helps anyone else.)

so, in that template that follows, there is another match statement:
<xsl:template match="collection">


This seems redundant: couldn't it be done without the first "match":
Code:
<xsl:stylesheet version="1.0" xmlns:xsl="[URL unfurl="true"]http://www.w3.org/1999/XSL/Transform">[/URL]
    <xsl:output method="html"/>
    <xsl:template match="/collection">
        <xsl:element name="collection">
             <xsl:apply-templates select="*"/>
        </xsl:element>
    </xsl:template>
    <xsl:template match="item">
        <xsl:element name="{@id}">
            <xsl:value-of select="."/>
        </xsl:element>
    </xsl:template>        
</xsl:stylesheet>

... yes, I just tested it, and it works identically. I think I'm starting to get it!

- Bill

Get the best answers to your questions -- See FAQ481-4875.
 
Like you realized, you can move directly to the first node /collection. This will work. As you get more used to XSLT, you will begin to see that how you develop your apply-templates will determine your ability to loop through the XML document. How you create your XPath for the apply-templates can determine if you match a template for each occurence of a node, for the first occurence, or a particular occurence (for example). So just look at your document as a visual tree and determine what nodes you want to hit and how you want to hit them. This is important because flow through an XML document is the biggest factor in speed. If you move through the document by a node and then its child nodes and then its child nodes, etc., this will be significantly faster than jumping around the XML document from node to node without a sequence.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top