extract element names with XSLT

S

Stefan Franke

Hi,
I've got the following simple XSLT stylesheet, that lists all the values of
the elements of any given XML file.

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="/">
<xsl:value-of select="current()"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

What I'm trying to find now is a way to extract the element name as well
(e.g. "TITLE" if the element is called <TITLE>).

Does anybody know how to do this?

Thanks,
Stefan
 
S

Stefan Franke

Thanks, but I'm afraid that I was cheering too early. I noticed that
"current()" selects all the values and not only one (so the for-each isn't
necessary).

Is there a way to iterate through the element names and element values one
after the other (without knowing the structure of the XML file)?

Thanks,
Stefan
 
J

Joris Gillis

Thanks, but I'm afraid that I was cheering too early. I noticed that
"current()" selects all the values and not only one (so the for-each isn't
necessary).

Is there a way to iterate through the element names and element values one
after the other (without knowing the structure of the XML file)?

Yes, but you have to realize that an element's value consist of the concatenation off all child text nodes up to any dept level.

Suppose you have this XML:
<root>
<title>hello</title>
<section>
<node>this is some <i>italic</i> text</node>
</section>
</root>


If you want this output:
root:hellothis is some italic text
title:hello
section:this is some italic text
node:this is some italic text
i:italic

Use:
<xsl:template match="/">
<xsl:for-each select="//*">
<xsl:value-of select="local-name()"/>:<xsl:value-of select="current()"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>

If you'd rather need this output:
root:
title:hello
section:
node:this is some text
i:italic

Use:
<xsl:template match="/">
<xsl:for-each select="//*">
<xsl:value-of select="local-name()"/>:<xsl:apply-templates select="current()/text()"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>


If you need yet another variant, just ask it.

regards,
 
S

Stefan Franke

If you'd rather need this output:
root:
title:hello
section:
node:this is some text
i:italic

Use:
<xsl:template match="/">
<xsl:for-each select="//*">
<xsl:value-of select="local-name()"/>:<xsl:apply-templates
select="current()/text()"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>


Thanks very much for this code. I just wondered if I was on the right path
with this code (acutally the second one) when I need an 'exact copy' of the
XML file. What I'm actually trying to do is to produce a stylesheet, that
just copies the XML file (I mustn't use xsl:copy and xsl:copy-of, though).

I cannot just put this code inside the for-each statement for sure:

<xsl:variable name="localname" value-of="local-name()" />
<xsl:variable name="text" value-of="current()/text()" />

<xsl:element name="%localname;">
<xsl:text>%text;</xsl:text>
</xsl:element>

Could you give me a hint on how to solve that please?

Thanks,
Stefan
 
J

Joris Gillis

Tempore 14:42:52 said:
What I'm actually trying to do is to produce a stylesheet, that
just copies the XML file (I mustn't use xsl:copy and xsl:copy-of, though).

Why not use them?
I cannot just put this code inside the for-each statement for sure:

<xsl:variable name="localname" value-of="local-name()" />
<xsl:variable name="text" value-of="current()/text()" />

<xsl:element name="%localname;">
<xsl:text>%text;</xsl:text>
</xsl:element>

You might put this:
<xsl:variable name="localname" select="local-name()" />
<xsl:variable name="text" select="current()/text()" />

<xsl:element name="{$localname}">
<xsl:value-of select="text()"/>
</xsl:element>

or more simple:

<xsl:element name="{local-name()}">
<xsl:apply-templates select="current()/text()"/>
</xsl:element>

regards,
 
S

Stefan Franke

<xsl:apply-templates select="current()/text()"/>
</xsl:element>

The problem with this approach is that an element is opened and right
afterwards closed again. So I cannot generate a tree structure with this
code. It's as if there should be some kind of recursive call or something
like that...

regards,
Stefan
 
J

Joris Gillis

Tempore 16:22:40 said:
The problem with this approach is that an element is opened and right
afterwards closed again. So I cannot generate a tree structure with this
code.

Yes you can, creating tree strucures is perfectly possible in XSLT.

The 'xsl:apply-templates' will help you achieve it. You should abandon the use of 'xsl:for-each' when you're dealing with recursion.

This code will make a pseudo copy of the input XML (only elements and text nodes are copied):

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="node()"/>
</xsl:element>
</xsl:template>

</xsl:stylesheet>


regards,
 
S

Stefan Franke

The 'xsl:apply-templates' will help you achieve it. You should abandon the
use of 'xsl:for-each' when you're dealing with recursion.


Ah, I see now how that cannot works with 'for-each'. Where I would use
'for-each' though, is when I want to extract the attributes:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="*">
<xsl:element name="{local-name()}">

<xsl:for-each select="current()/@*">
<xsl:variable name="i" select="string(current()/@*)" />
<xsl:attribute name="a">$i</xsl:attribute>
</xsl:for-each>

<xsl:apply-templates select="node()"/>
</xsl:element>
</xsl:template>

</xsl:stylesheet>

I've got two problems with this code. Firstly, I cannot get this variable $i
to show its value (I always get '$i' with Saxon 8.2) and secondly, how can I
retrieve the name of the attribute with an XPath query?

Thanks,
Stefan
 
J

Joris Gillis

<xsl:variable name="i" select="string(current()/@*)" />
<xsl:attribute name="a">$i</xsl:attribute>
</xsl:for-each>

I've got two problems with this code. Firstly, I cannot get this variable $i
to show its value (I always get '$i' with Saxon 8.2)
If you want the value of something... use 'xsl:value-of' ...that does make sense:)

Use:
<xsl:variable name="i" select="string(.)" />
<xsl:attribute name="a"><xsl:value-of select="$i"/></xsl:attribute>
Or easier:
and secondly, how can I
retrieve the name of the attribute with an XPath query?
use local-name(): it works for attributes as well as elements.


So the whole code is this:
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:for-each select="current()/@*">
<xsl:attribute name="{local-name()}"><xsl:value-of select="."/></xsl:attribute>
</xsl:for-each>
<xsl:apply-templates select="node()"/>
</xsl:element>
</xsl:template>

but tell me, why not use 'xsl:copy-of' or 'xsl:copy'?

regards,
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,042
Latest member
icassiem

Latest Threads

Top