Go Premium for a chance to win a PS4. Enter to Win

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 320
  • Last Modified:

xml table with two columns


i have the data in following format

<table_node>

<website>
<name>yahoo</name>
<url>www.yahoo.com</url>
</website>

<website>
<name>google</name>
<url>www.google.com</url>
</website>

<website>
<name>altavista</name>
<url>www.altavista.com</url>
</website>

<website>
<name>alltheweb</name>
<url>www.alltheweb.com</url>
</website>


<website>
<name>taeoma</name>
<url>www.taeoma.com</url>
</website>

</table_node>

i am trying to write xsl to transform the data in tabular format. i am able to do this using one column but i need
to arrange in 2 columns  so that i can create table of hyperlinks.

any way to do this?
0
sarika1977
Asked:
sarika1977
  • 7
  • 5
  • 2
  • +1
1 Solution
 
rishiskCommented:
Can you paste your xsl code here.
0
 
metalmickeyCommented:
Heres a xsl that will build 2 columns

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
  <xsl:template match="/">
    <html>
      <title/>
      <head/>
      <body>
        <table>
          <tr>
            <th>Name</th>
            <th>URL</th>
          </tr>
          <xsl:apply-templates/>
        </table>
      </body>
    </html>
  </xsl:template>
  <xsl:template match="website">
    <tr>
      <xsl:apply-templates/>
    </tr>
  </xsl:template>
  <xsl:template match="name">
    <td align="left">
      <xsl:apply-templates/>
    </td>
  </xsl:template>
  <xsl:template match="url">
    <td align="left">
 <!--     <a><xsl:attribute name="href"><xsl:value-of select="."/></xsl:attribute>   -->
        <xsl:apply-templates/>
<!--      </a>   -->
    </td>
  </xsl:template>
</xsl:stylesheet>


If you want the URL's to become links, just remove the comments from around the

<a> <!-- in the xsl  --> and the </a>


MM
0
 
sarika1977Author Commented:
thanks mm !
but it doesn't solve my problem.

it arranges data like
 
yahoo
google
altavista
alltheweb
taeoma

etc.

I'm interested in arranging data with hyperlinks like

yahoo            alltheweb
google            taeoma
altavista        --

0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
rishiskCommented:
Somebody had a similar requirement. Check this out.

http://www.codeguru.com/forum/showthread.php?threadid=262467
0
 
sarika1977Author Commented:
rishisk

yep, the page has similar requirement but it doesn't arrange in the sequence i am looking for.

say
1
2
3
4
5
6

i need to arrange it in
1    4
2    5
3    6

order.

i am newbie and do not know how to achieve this.
0
 
rdcproCommented:
That's actually fairly easy, but a tough one for a newbie! ;^) This will do it:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes"/>
    <xsl:variable name="vMidPoint" select="ceiling(count(table_node/website) div 2)"/>
    <xsl:template match="table_node">
        <table border="1" cellpadding="5" cellspacing="0">
            <xsl:apply-templates select="website[position() &lt;= $vMidPoint]"/>
        </table>
    </xsl:template>
    <xsl:template match="website">
        <tr>
            <td><a href="{url}"><xsl:value-of select="name"/></a></td>
            <td>
                <xsl:if test="following-sibling::website[$vMidPoint]">
                    <a href="{following-sibling::website[$vMidPoint]/url}"><xsl:value-of select="following-sibling::website[$vMidPoint]/name"/></a>
                </xsl:if>
            </td>        
        </tr>
    </xsl:template>

</xsl:stylesheet>



and produces this HTML:

<table border="1" cellpadding="5" cellspacing="0">
    <tr>
        <td>
            <a href="www.yahoo.com">yahoo</a>
        </td>
        <td>
            <a href="www.alltheweb.com">alltheweb</a>
        </td>
    </tr>
    <tr>
        <td>
            <a href="www.google.com">google</a>
        </td>
        <td>
            <a href="www.taeoma.com">taeoma</a>
        </td>
    </tr>
    <tr>
        <td>
            <a href="www.altavista.com">altavista</a>
        </td>
        <td>
        </td>
    </tr>
</table>


Regards,
Mike Sharp

http://dev.rdcpro.com


0
 
rdcproCommented:
Actually, to add the "http://" to the link modify this line like:

          <td><a href="http://{url}"><xsl:value-of select="name"/></a></td>

and do the same for the right hand column:

<a href="http://{following-sibling::website[$vMidPoint]/url}"><xsl:value-of select="following-sibling::website[$vMidPoint]/name"/></a>

Regards,
Mike Sharp
0
 
metalmickeyCommented:
Hi Mike

can you anotate the template for me so i can see whats happening.

Thanks

MM
0
 
rdcproCommented:
Sure, but it's not all that obscure when you get the core idea.  First, I find the midpoint.  If there are an odd number of items, then I want the "extra" item in the first group.  So I use the ceiling() XPath function:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes"/>
    <xsl:variable name="vMidPoint" select="ceiling(count(table_node/website) div 2)"/>

That way, $vMidPoint will always tell me what the position of the first item in the second column is.  To build my table, I want a nodeset which only includes those items that are less than or equal to the midpoint.  In the example, it's the first three items.  So I apply-templates for <website> where the position is less than or equal to $vMidPoint (which is 3, in the example).


    <xsl:template match="table_node">
        <table border="1" cellpadding="5" cellspacing="0">
            <xsl:apply-templates select="website[position() &lt;= $vMidPoint]"/>
        </table>
    </xsl:template>


The next template is fairly simple, but contains the key to the process. The first TD is easy...just the current node, right?

    <xsl:template match="website">
        <tr>
            <td><a href="{url}"><xsl:value-of select="name"/></a></td>

The next TD is the tricky one.  The following-sibling axis counts from the current node forward in *document* order.  It is important to realize that it works on the original nodeset of the original XML, not the set of nodes I'm working with in my apply-templates!  So what I want is the 3rd node after the current node, whatever it is, because my midpoint is 3.  So if I'm processing the very first node, in the 2nd TD I want the *fourth* node.  When I process the 2nd node, I want the 5th node, and so on.  I start off with an xsl:if, because I don't want to put a blank <a> tag in the output unless there is a node to process at that point.  If I was working on the first <website> node, it would be similar to this XPath expression (assuming it were legal):

"//website[position() = (current()/position() + 3)]"

but since you can't use current()/position() in XPath, you have the following-sibling axis, which means essentially, "starting from the current position..."

            <td>
                <xsl:if test="following-sibling::website[$vMidPoint]">
                    <a href="{following-sibling::website[$vMidPoint]/url}"><xsl:value-of select="following-sibling::website[$vMidPoint]/name"/></a>
                </xsl:if>
            </td>        
        </tr>
    </xsl:template>

</xsl:stylesheet>

And that's all there is to it!

Regards,
Mike Sharp
0
 
rdcproCommented:
I should have said:

"That way, $vMidPoint will always tell me the position immediately before the first item in the second column."

meaning, if there are seven items, $vMidPoint is 4, and the first item in the second column will be item #5

Regards,
Mike Sharp
0
 
sarika1977Author Commented:
thank you very much mike, it works great  but gives another problem when i embedd inside the another 'template match' statement.

can't I use nested 'template match' ?
0
 
rdcproCommented:
No you can't.  xsl:template must be an immediate child of <xsl:stylesheet> (or xsl:transform, if you use that one)

Just extract the logic from my example, and implement it in your XSLT, or post your XSLT.

Regards,
Mike Sharp
0
 
sarika1977Author Commented:
i tried something like the following. it creates table with 2 columns  inside xsl:template but doesn't order properly.

<tr>
<xsl:for-each select="table_node/website">
<xsl:variable name="vCurPosition" select="position()"/>
<xsl:choose>
<xsl:when test="$vCurPosition mod 2 = 0">
<td><xsl:value-of select="name"/>

</td>
&lt;/tr> &lt;tr>
</xsl:when>
<xsl:otherwise>
<td> <xsl:value-of select="name"/>
</td>
</xsl:otherwise>
</xsl:choose>

</xsl:for-each>

</tr>
0
 
rdcproCommented:
Any time you find yourself doing this:

</td>
&lt;/tr> &lt;tr>

you're doing XSLT wrong.  There is never a case where you have to resort to this type of situation in XSLT.  In fact, your output will actually be:

</td>
&lt;/tr> &lt;tr>

and not:

</td>
</tr> <tr>

I can't stress this enough.  Do not resort to entities in the XSLT, or other tricks like that simply in order to get around the well-formedness constraints.  I have a number of examples how to build tables like this on my site.  For example:

http://dev.rdcpro.com/Members/rdcpro/snippets/xpathgrouping/
and
http://dev.rdcpro.com/Members/rdcpro/snippets/xpathgrouping2/



The modulo approach is used to create tables that are ordered like:

1    2
3    4
5    6

not in the newspaper style you said you're trying to achieve:

1    4
2    5
3    6

Perhaps you could post your complete XSLT?  It's very difficult to figure out what you're trying to do from a snippet.

Regards,
Mike Sharp
0
 
sarika1977Author Commented:
thanks you very much mike for the tip. i guess i need to work a lot for
learning xsl.

what i'm trying to achive is:

i have a list

1
2
3
4
5
6
7

i am trying to put it in a table like

1  5
2  6
3  7
4

i need to embedd this code inside the existing xsl which already has
'xsl:template match' statement. i don't want to change the existing functionality and achive the desired effect.

thanks
0
 
rdcproCommented:
My original code does just that...read the annotated version I posted for MetalMickey, it should explain how this is done.  You'll have to integrate my approach into your XSLT, but without seeing your XSLT, I can't tell you specifically how it would be done.  But you won't use the mod operator for it.  You'll add a global variable that identifies the mid point, then apply templates (or use a for-each) on the nodes whose position is up to and including the midpoint.  This builds the rows, and the first TD cell.  The second TD cell is built using the node from the original nodes that is at the current node's position plus the midpoint.

Regards,
Mike Sharp
0

Featured Post

Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

  • 7
  • 5
  • 2
  • +1
Tackle projects and never again get stuck behind a technical roadblock.
Join Now