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

XSLT to HTML transformation - compare two different nodes 1

Status
Not open for further replies.

rvijayendran

Programmer
Hi.. I have a XML :

<CategoryDataSet>
<Group>
<dim_tag>MKT</dim_tag>
<group_name>M.4135930</group_name>
<group_sequence>1</group_sequence>
</Group>
<Group>
<dim_tag>MKT</dim_tag>
<group_name>M.4066346</group_name>
<group_sequence>2</group_sequence>
</Group>
<Item>
<dim_tag>MKT</dim_tag>
<item_tag>M.4135930</item_tag>
<item_short_desc>KWIK TRIP</item_short_desc>
</Item>
<Item>
<dim_tag>MKT</dim_tag>
<item_tag>M.4066346</item_tag>
<item_short_desc>FOOD LION</item_short_desc>
</Item>
</CategoryDataSet>

I want this transformed to the below format :

dim_tag group_name group_sequence item_short_desc
MKT M.4135930 1 KWIK TRIP
MKT M.4066346 2 FOOD LION


Please suggest a way. I had tried with this XSL, but my problem is i m not able to match the CategoryDataSet/Group/group_name with the CategoryDataSet/Item/item_tag values.


<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl=" <xsl:template match="/">
<html>
<body>
<h2>Group Data Set</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th align="left">dim_tag</th>
<th align="left">group_name</th>
<th align="left">group_sequence</th>
<th align="left">item_short_desc</th>

</tr>
<xsl:for-each select="CategoryDataSet/Group">
<tr>
<td>
<xsl:value-of select="dim_tag"/>
</td>
<td>
<xsl:value-of select="group_name"/>
</td>
<td>
<xsl:value-of select="group_sequence"/>
</td>
<td>

<xsl:if test="group_name = CategoryDataSet/Item/item_tag"> <xsl:value-of select="CategoryDataSet/Item/item_short_desc"/>
</xsl:if> </td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
 
[0] The cardinal method is to first create an index table matching item_tag's value with its sibling item_short_desc. Concretely it is by those constructions as shown.

[1] First create a key element (xsl:key). It is a top-level element, such as put it below the xsl:stylesheet line.
[tt] <xsl:key name="tag_desc" match="/CategoryDataSet/Item/item_short_desc" use="../item_tag" />[/tt]

[2] Then in the xsl:for-each construction, replace that line by this.
[tt] <xsl:value-of select="concat(key('tag_desc',group_name)[1],'&#xa0;')" />[/tt]

[2.1] I add a finer detail of appending a &nbsp; to it just in case, if there is no match, the rendering wouldn't be compromised. But that is not the point of the construction.
 
Dear tsuji

It worked !!!! Amazing !!!
happy.gif

Thanks a lot...

Just one more help :)

If i want to sort that particular column alphabetically, how do i go about it ? ( i mean the column which has these values <xsl:value-of select="concat(key('tag_desc',group_name)[1],'&#xa0;')" />)
 
[3] >If i want to sort that particular column alphabetically, how do i go about it ?
You can just add the xsl:sort element immediately under the xsl:for-each element, like this.
[tt]
<xsl:for-each select="CategoryDataSet/Group">
[blue]<xsl:sort select="key('tag_desc',group_name)" order="ascending" />[/blue]
<tr>
<!-- etc etc -->
</tr>
</xsl:for-each>[/tt]

[3.1] ordering attribute takes on two alternatives: "ascending" (default) or "descending".
 
Dear tsuji

I have modified the XML as below :

<CategoryDataSet>
<Group>
<dim_tag>MKT</dim_tag>
<group_name>Account Detail</group_name>
<group_sequence>1</group_sequence>
<group_parent>0</group_parent>
</Group>
<Group>
<dim_tag>MKT</dim_tag>
<group_name>M.4135930</group_name>
<group_sequence>2</group_sequence>
<group_parent>1</group_parent>
</Group>
<Group>
<dim_tag>MKT</dim_tag>
<group_name>M.4066346</group_name>
<group_sequence>3</group_sequence>
<group_parent>1</group_parent>
</Group>
<Group>
<dim_tag>MKT</dim_tag>
<group_name>Account Name</group_name>
<group_sequence>4</group_sequence>
<group_parent>0</group_parent>
</Group>
<Item>
<dim_tag>MKT</dim_tag>
<item_tag>M.4135930</item_tag>
<item_short_desc>KWIK TRIP</item_short_desc>
</Item>
<Item>
<dim_tag>MKT</dim_tag>
<item_tag>M.4066346</item_tag>
<item_short_desc>FOOD LION</item_short_desc>
</Item>
</CategoryDataSet>

How should i modify my XSl so that the output appears in the following format :

dim_tag    group_name    group_sequence    item_short_desc
MKT Account Detail 1                 
MKT 2     KWIK TRIP
MKT 3     FOOD LION
MKT Account Name 4
 
U can see that, group_sequence and group_parent also have a tree or folder sort of connection.
 
we need to sort the item_short_desc like follows:

This is the desired ouptut :

dim_tag group_name group_sequence item_short_desc
MKT Account Detail 1
MKT 3 FOOD LION
MKT 2 KWIK TRIP
MKT Account Name 4


But, if we use a general sort statement, then the output will be:

dim_tag group_name group_sequence item_short_desc
MKT Account Detail 1
MKT Account Name 4
MKT 3 FOOD LION
MKT 2 KWIK TRIP
 
[4] The now revised order is not much difference with random order without clear logic layout. I mean, in a sense, you can make out three xsl:for-each, one selecting "Account Detail", one for "M..." and one for "Account Name". But that won't be very satisfactory.

[4.1] In fact, you should add some info reflecting naturally the order one is targetting. For instance, add a tag to Group amount to reflecting the order, say <group_level>, like this.
[tt]
<Group>
<dim_tag>MKT</dim_tag>
<group_name>Account Detail</group_name>
<group_sequence>1</group_sequence>
<group_parent>0</group_parent>
[blue]<group_level>0</group_level>[/blue]
</Group>
<Group>
<dim_tag>MKT</dim_tag>
<group_name>M.4135930</group_name>
<group_sequence>2</group_sequence>
<group_parent>1</group_parent>
[blue]<group_level>1</group_level>[/blue]
</Group>
<Group>
<dim_tag>MKT</dim_tag>
<group_name>M.4066346</group_name>
<group_sequence>3</group_sequence>
<group_parent>1</group_parent>
[blue]<group_level>1</group_level>[/blue]
</Group>
<Group>
<dim_tag>MKT</dim_tag>
<group_name>Account Name</group_name>
<group_sequence>4</group_sequence>
<group_parent>0</group_parent>
[blue]<group_level>2</group_level>[/blue]
</Group>
[/tt]
[4.2] That change may not be acceptable or out of your control. But, the essential is that it reflects the real business logic. In that regard, the <group_sequence> seems not be. If it reflects the document order of the Group, it is not a good design. Maybe you can use that slot of freedom to define the group_level setting in there rather than adding a new tag.

[4.3] With that defined, you can then naturally do the sorting in the xsl, like this.
[tt]
<xsl:for-each select="CategoryDataSet/Group">
[blue]<xsl:sort select="group_level" order="ascending" />[/blue]
<xsl:sort select="key('tag_desc',group_name)" order="ascending" />
<!-- etc etc -->
</xsl:for-each>
[/tt]
And that would be done.
 
Dear tsuji

The change u mentioned in the XML (of adding group_level tag) is out of my control.

The exact xml is this only:

<CategoryDataSet>
<Group>
<dim_tag>MKT</dim_tag>
<group_name>Account Detail</group_name>
<group_sequence>1</group_sequence>
<group_parent>0</group_parent>
</Group>
<Group>
<dim_tag>MKT</dim_tag>
<group_name>M.4135930</group_name>
<group_sequence>2</group_sequence>
<group_parent>1</group_parent>
</Group>
<Group>
<dim_tag>MKT</dim_tag>
<group_name>M.4066346</group_name>
<group_sequence>3</group_sequence>
<group_parent>1</group_parent>
</Group>
<Group>
<dim_tag>MKT</dim_tag>
<group_name>Account Name</group_name>
<group_sequence>4</group_sequence>
<group_parent>0</group_parent>
</Group>
<Group>
<dim_tag>MKT</dim_tag>
<group_name>M.4066159</group_name>
<group_sequence>5</group_sequence>
<group_parent>4</group_parent>
</Group>
<Group>
<dim_tag>MKT</dim_tag>
<group_name>M.4066163</group_name>
<group_sequence>6</group_sequence>
<group_parent>4</group_parent>
</Group>
<Item>
<dim_tag>MKT</dim_tag>
<item_tag>M.4135930</item_tag>
<item_short_desc>KWIK TRIP</item_short_desc>
</Item>
<Item>
<dim_tag>MKT</dim_tag>
<item_tag>M.4066346</item_tag>
<item_short_desc>FOOD LION</item_short_desc>
</Item>
<Item>
<dim_tag>MKT</dim_tag>
<item_tag>M.4066159</item_tag>
<item_short_desc>TOPS TOTAL</item_short_desc>
</Item>
<Item>
<dim_tag>MKT</dim_tag>
<item_tag>M.4066163</item_tag>
<item_short_desc>ALPHA CENTRA</item_short_desc>
</Item>
</CategoryDataSet>


and the required output is this :

dim_tag group_name group_sequence item_short_desc
MKT Account Detail 1
MKT 3 FOOD LION
MKT 2 KWIK TRIP
MKT Account Name 4
MKT 6 ALPHA CENTRA
MKT 5 TOPS TOTAL


I agree with u that it makes no logic with the above XML, but since it is definitely out of my control, i have to make the requirement work, by hook or crook !!

I know i m taxing u too much, but please do help !!
 
[5] new xml data

[5.0] I must say the newly presented xml has some generic difference from the one precedent, it has an impact on the approach but the arbitriness remains the same.

[5.1] I try to preserve some generality all the same with the degree of freedom you can freely make the number of M... group_name varying following a group_name starting with Account.

[5.2] This is how.
[tt]
[blue]<xsl:key name="tag_desc" match="/CategoryDataSet/Item/item_short_desc" use="../item_tag" />[/blue]
<xsl:template match="/">
<html>
<body>
<h2>Group Data Set</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th align="left">dim_tag</th>
<th align="left">group_name</th>
<th align="left">group_sequence</th>
<th align="left">item_short_desc</th>

</tr>
[red]<!--[/red]
<xsl:for-each select="CategoryDataSet/Group">
[red]-->[/red]
[blue]<xsl:for-each select="CategoryDataSet/Group[starts-with(group_name,'Account')]">[/blue]
<tr>
<td>
<xsl:value-of select="dim_tag"/>
</td>
<td>
<xsl:value-of select="group_name"/>
</td>
<td>
<xsl:value-of select="group_sequence"/>
</td>
<td>
[blue]<xsl:value-of select="'&#xa0;'" />[/blue]
</td>
</tr>
[blue]<xsl:variable name="cnt" select="count(following-sibling::*[starts-with(group_name,'Account')])" />[/blue]
[blue]<xsl:for-each select="following-sibling::*[(starts-with(group_name,'M')) and (count(following-sibling::*[starts-with(group_name,'Account')])=$cnt)]">
<xsl:sort select="key('tag_desc',group_name)" order="ascending" />[/blue]
[blue]<tr>
<td>
<xsl:value-of select="dim_tag"/>
</td>
<td>
<xsl:value-of select="group_name"/>
</td>
<td>
<xsl:value-of select="group_sequence"/>
</td>
<td>
<xsl:value-of select="concat(key('tag_desc',group_name)[1],'&#xa0;')" />
</td>
</tr>
</xsl:for-each>[/blue]
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
[/tt]
[5.3] It is considerably harder to follow. You pay a price for bending the process to adopt to a design, good or bad.
 
[5.4] If you have digested the renderment of [5.2], you can now use named template to make it neater. Like this.

[5.4.1] Make a named template, proc, say.
[tt]
<xsl:template name="proc">
<tr>
<td>
<xsl:value-of select="dim_tag"/>
</td>
<td>
<xsl:value-of select="group_name"/>
</td>
<td>
<xsl:value-of select="group_sequence"/>
</td>
<td>
<xsl:value-of select="concat(key('tag_desc',group_name)[1],'&#xa0;')" />
</td>
</tr>
</xsl:template>
[/tt]
[5.4.2] Then the main body of the template is simplified like this.
[tt]
<xsl:template match="/">
<html>
<body>
<h2>Group Data Set</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th align="left">dim_tag</th>
<th align="left">group_name</th>
<th align="left">group_sequence</th>
<th align="left">item_short_desc</th>
</tr>
<xsl:for-each select="CategoryDataSet/Group[starts-with(group_name,'Account')]">
<xsl:call-template name="proc" />
<xsl:variable name="cnt" select="count(following-sibling::*[starts-with(group_name,'Account')])" />
<xsl:for-each select="following-sibling::*[(starts-with(group_name,'M')) and (count(following-sibling::*[starts-with(group_name,'Account')])=$cnt)]">
<xsl:sort select="key('tag_desc',group_name)" order="descending" />
<xsl:call-template name="proc" />
</xsl:for-each>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
[/tt]
That would look more tractable.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top