Fog Creek Software
Discussion Board




XML to XML, XSLT question

beginning XML:

<root>
<Foo>
<a>12345</a>
<Bar>
<1>abcde</1>
<2>bcdef</2>
<3>cdefg</3>
</Bar>
<Bar>
<1>defgh</1>
<2>efghi</2>
<3>fghij</3>
</Bar>
</Foo>
</root>

When translating, I want to convert a to aa:

<aa>12345</aa>

(that's easy).

But, I also want to when translating the Bar node, include the element aa.  There can be 0 to n Bar nodes.  Imagine we are translating the Bar node to the Baz node:

<Baz>
<aa>12345</aa>
<11>abcde</11>
<22>bcdef</22>
<33>cdefg</33>
</Baz>

I can't seem to find any examples (easily) of an element being repeated.

The original xml would look something like this:

<Fooo>
<aa>12345</aa>
<Baz>
<aa>12345</aa>
<11>abcde</11>
<22>bcdef</22>
<33>cdefg</33>
</Baz>
<Baz>
<aa>12345</aa>
<11>defgh</11>
<22>efghi</22>
<33>fghij</33>
</Baz>
</Fooo>

XSL newbie
Monday, June 14, 2004

er, copy or get the value of ../a?

mb
Tuesday, June 15, 2004

Yeah. Either:

<xsl:variable name="aa" select="root/Foo/a"/>

at the start of your stylesheet, or

<xsl:value-of select="../a"/>

in your Bar matching template. Something like that.

Thom Lawrence
Tuesday, June 15, 2004

Newbie,

You can repeat an element as many times as you like, that's no problem.

I think that the problem may be that you are trying to reference the aa element in your XSL.  'aa' is part of the output, so you cannot reference it.

Perhaps you could use an xsl:variable element? http://www.w3schools.com/xsl/el_variable.asp

Ged Byrne
Tuesday, June 15, 2004

Newbie,

Ged is absolutely right. If you only have one <a> node, and you want it to be converted to <aa> and placed as the first child node of every <bar> node, you want to use the xsl:variable.

So the XSL you would want to use would be like:

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

    <xsl:template match="/">
          <xsl:apply-templates select="root/Foo"/>
    </xsl:template>

    <xsl:template match="root/Foo">
    <xsl:variable name="aValue" select="a"/>
        <aa><xsl:value-of select="$aValue"/></aa>
        <xsl:for-each select="Bar">
            <aa><xsl:value-of select="$aValue"/></aa>
            <11><xsl:value-of select="1"/></11>
            <22><xsl:value-of select="2"/></22>
            <33><xsl:value-of select="3"/></33>
        </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

Cory

CF
Tuesday, June 15, 2004

Ok, now that I'm at work and have my XSL Transformer, I realized I posted that last one a little wrong. Here is the XML document I used (since <1>, et al,  isn't valid):

<root>
<Foo>
<a>12345</a>
<Bar>
<a1>abcde</a1>
<a2>bcdef</a2>
<a3>cdefg</a3>
</Bar>
<Bar>
<a1>defgh</a1>
<a2>efghi</a2>
<a3>fghij</a3>
</Bar>
</Foo>
</root>


And the matching XSL:

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

    <xsl:template match="/">
          <root>
          <xsl:apply-templates select="root/Foo"/>
          </root>
    </xsl:template>

    <xsl:template match="root/Foo">
    <xsl:variable name="aValue" select="a"/>
        <aa><xsl:value-of select="$aValue"/></aa>
        <xsl:for-each select="Bar">
          <baz>
            <aa><xsl:value-of select="$aValue"/></aa>
            <a11><xsl:value-of select="a1"/></a11>
            <a22><xsl:value-of select="a2"/></a22>
            <a33><xsl:value-of select="a3"/></a33>
          </baz>
        </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

Which produces as output:

<root>
    <aa>12345</aa>
    <baz>
        <aa>12345</aa>
        <a11>abcde</a11>
        <a22>bcdef</a22>
        <a33>cdefg</a33>
    </baz>
    <baz>
        <aa>12345</aa>
        <a11>defgh</a11>
        <a22>efghi</a22>
        <a33>fghij</a33>
    </baz>
</root>

Hope that helps!

Cory

CF
Tuesday, June 15, 2004

mb,

er, I'm a, er, newbie, who has never worked with or used XSL, er, in my life (C++ programmer, PDA's).  On top of that, er, I'm modifying a current, er, xsl file, er, so there is an existing structure that I have to, er, work with.  I didn't want to, er, rewrite, but just, er, modify, er, easily.

As with everything with me, er, sometimes I just need an a-ha! moment.  This thread has, er, helped!  Thanks All!

Details will be in my next post (sorry, I'm just being a wise-guy because of the smarty pants "er" comment). :)

XSL newbie
Tuesday, June 15, 2004

The xml *really* looks something like this:

<root>
<top>
<data>1</data>
<data2>2</data2>
<middle>
<more_data>1</more_data>
<even_more>2</even_move>
<repeated>3</repeated>  <-- more repeated values
<repeated>3</repeated>
<other_repeated>4</other_repeated> <-- other repeated
<other_repeated>4</other_repeated>
<Foo>
<c>cdefg</c>
<b>bcdef</b>
<a>12345</a>
<a>12345</a> <-- notice, it's repeated (new value)
<Bar>
<1>abcde</1>
<2>bcdef</2>
<3>cdefg</3>
</Bar>
<Bar>
<1>defgh</1>
<2>efghi</2>
<3>fghij</3>
</Bar>
</Foo>
</middle>
</top>
</root>

The new stuff is:
<a>12345</a>
<a>12345</a> <-- notice, it's repeated (new value)
<Bar>
<1>abcde</1>
<2>bcdef</2>
<3>cdefg</3>
</Bar>
<Bar>
<1>defgh</1>
<2>efghi</2>
<3>fghij</3>
</Bar>

In the xsl I'm looking at, the guy did something like this:

<xsl:include href="another_xsl.xsl"/>

<xsl:template match="/root">
    <xsl:element name="new_root">
        <xsl:apply-templates                select="top"/>
    </xsl:element>
</xsl:template>

<xsl:template match="top">
    <xsl:element name="new_top">
        <xsl:apply-templates select="data|data2"/>
        <xsl:apply-templates select="middle"/>
    </xsl:element>
</xsl:template>

<xsl:template match="middle">
    <xsl:apply-templates select="Foo[position()=last()]"/>
    <xsl:apply-templates select="Bar"/> <-- NEW! I added this!
    <!--there are others, buy why worry about them-->
</xsl:template>

*****
the first thing that puzzled me when I first saw this was the position()=last().  Especially since I haven't seen any input XML that had more than 1 "Foo" nodes.
*****

Inside the "another_xsl.xsl" I see:

<!--tags under top-->
<xsl:template match="data">
    <xsl:element name="datad">
        <xsl:value-of select="."/>
    </xsl:element>
</xsl:template>

<xsl:template match="data2">
    <xsl:element name="data2d">
        <xsl:value-of select="."/>
    </xsl:element>
</xsl:template>

<!--tags under middle-->
<xsl:template match="more_data">
    <xsl:element name="more_data_m">
        <xsl:value-of select="."/>
    </xsl:element>
</xsl:template>

<xsl:template match="even_more">
    <xsl:element name="even_more_even">
        <xsl:value-of select="."/>
    </xsl:element>
</xsl:template>

<!--I don't see the repeated tag (the two of them) nor do I see the other_repeated tag (the two of them)-->

*****
That was another question I had, since he didn't explicitly touch on those tags.  I wonder if that had anything to do with the position()=last() stuff.
*****

<!--Tags under Foo-->
<xsl:template match="c[position()=1]">
    <xsl:element name="cc">
        <xsl:value-of select="."/>
    </xsl:element>
</xsl:template>

<xsl:template match="b[position()=1]">
    <xsl:element name="bb">
        <xsl:value-of select="."/>
    </xsl:element>
</xsl:template>

*****
Again, more "position" stuff!
*****

So, because "a" (under Foo) is one of the new tags, I followed right along and added:

<xsl:template match="a[position()=1]">
    <xsl:element name="aa">
        <xsl:value-of select="."/>
    </xsl:element>
</xsl:template>

But, for some reason, the repeated a tag in the origin xml was showing up in the output (but with no tags around it of course, it looked like some random tax between tags).

So, I added this to get rid of the random text:

<xsl:template match="a[position()!=1]"/>

...

Now, it was (for me) time to handle the new "Bar" tags.

For some reason these two things were in my head:

1) The xsl processes the origin XML from top to bottom.  I don't know why, I just made that silly assumption.  So, I figured I'd need a variable to store "a" because it gets to those tags before "Foo" (if you are reading top to bottom).  So, I was thinking variable!

2) I didn't really pay attention to the "." in the <xsl:value-of select="."/> thingies I saw so often.  It didn't occur to me that I could do things like: "../../."  Like being on a dos prompt.  Don't know why, maybe b/c it was 11pm.

So, I declared a variable at the top of the xsl (not the included xsl).  Because I wanted to make it global:

<xsl:variable name="a_var"/>

notice I didn't "select" the value yet.  For some reason, I tried to do something like this (this is the new xsl from above with one added line):

<xsl:template match="a[position()=1]">
    <xsl:element name="aa">
        <xsl:value-of select="."/>
        <xsl:value-of select="$prod=."/> <--NEW!
    </xsl:element>
</xsl:template>

I tried to do the assigment right then.  When it's really accessing the a tag and doing something with it.  It seemed smart at the time.

<xsl:template match="Bar">
    <xsl:element name="aa">
        <xsl:value-of select = "$prod"/>
    </xsl:elemant>
</xsl:template>

But, that doesn't work.  All I ever get in my translation is <aa/> (empty tag).

I figured, because of my 2 assumptions, that $prod would be assigned a value before it got to this.  Apparently, that's not the case.

But, really, after my "A-ha!" moment for #2, I discovered that I didn't need the variable.  I could do this:

<xsl:template match="Bar">
    <xsl:element name="aa">
        <xsl:value-of select = "../a[position()=1]"/>
    </xsl:elemant>
</xsl:template>

No need for a variable, I just grabbed the value.  Duh! :)

Anyone, feel free, to examine why this guy did what he did ... especially the position()=last() stuff.

Thanks!

XSL newbie
Tuesday, June 15, 2004

*  Recent Topics

*  Fog Creek Home