[XSL-LIST Mailing List Archive Home] [By Thread] [By Date]

[xsl] Re: How to get nodes with maximum attributes ??


Subject: [xsl] Re: How to get nodes with maximum attributes ??
From: Dipesh Khakhkhar <dkhakhkh@xxxxxxxxxxxxxxx>
Date: Sun, 24 Aug 2003 23:03:20 -0400

Hi Dimitre,

Thanks a lot for responding and explaining and providing two solutions to 
solve my problem.

Thanks Mike to give me hints to use EXSLT. I will go for that as well if this 
tough problem is not achieved with usual methods.

I used Wendell's solution to find the node with maximum attributes and 
printing them as per my requirement which i stated in my previous postions. 
Here is the xsl which I am writting.

My Input XML is
===============

<?xml version="1.0" encoding="UTF-8"?>
<AEXDATAEXTRACT DTD_VERSION="2.2" EXTRACT_START_DATETIME="1/9/2003 4:49:39 PM" 
EXTRACT_TYPE="FULL" EXTRACT_COLLECTION="">
<RESOURCE_TYPE GUID="{493435f7-3b17-4c4c-b07f-c23e7ab7781f}" NAME="Computer" 
DESCRIPTION="Asset Type definition for Computer" SOURCE="Asset" 
CREATED_DATE="7/16/2002 5:22:23 PM" MODIFIED_DATE="9/23/2002 2:17:48 PM" 
DELETED="0">
<RESOURCE GUID="{C116FCBF-5B94-4F15-BF95-5795DBD384CD}" NAME="ALTIRISTEST_CPQ" 
SOURCE="" SITE_CODE="756win" DOMAIN="FIDD" SYSTEM_TYPE="Win32" 
OS_NAME="Microsoft Windows XP" OS_TYPE="Professional" OS_VERSION="5.1" 
OS_REVISION="Service Pack 1" LAST_LOGON_USER="" LAST_LOGON_DOMAIN="">
<INVENTORY>
<ASSET>
<IDENTIFICATION>
<ATTRIBUTE NAME="Name">ALTIRISTEST_CPQ</ATTRIBUTE>
<ATTRIBUTE NAME="Domain">FIDDOMRTLSLC</ATTRIBUTE>
<ATTRIBUTE NAME="Altkey1" NULL="FALSE" />
<ATTRIBUTE NAME="Altkey2">00-02-A5-1A-67-A6</ATTRIBUTE>
</IDENTIFICATION>

<CLASS NAME="Client_Agent">
<OBJECT>
<ATTRIBUTE NAME="Agent Name">Altiris eXpress NS Client</ATTRIBUTE>
<ATTRIBUTE NAME="Product Version">5.5.0.517</ATTRIBUTE>
<ATTRIBUTE NAME="Build Number">517</ATTRIBUTE>
<ATTRIBUTE NAME="Install Path">C:\Program Files\Altiris\eXpress\NS 
Client</ATTRIBUTE>
</OBJECT>
<OBJECT>
<ATTRIBUTE NAME="Agent Name">Altiris eXpress Inventory Solution</ATTRIBUTE>
<ATTRIBUTE NAME="Product Version">5.5.0.424</ATTRIBUTE>
<ATTRIBUTE NAME="Build Number">424</ATTRIBUTE>
<ATTRIBUTE NAME="Install Path">C:\Program Files\Altiris\eXpress\NS 
Client\Software Delivery\Software 
Packages\{01B54EB5-3679-4C73-9E10-E169D5A5EC59}</ATTRIBUTE>
</OBJECT>
</CLASS>
<CLASS NAME="Inventory_Results">
<OBJECT>
<ATTRIBUTE NAME="Collection Time">1/9/2003 3:06:56 AM</ATTRIBUTE>
<ATTRIBUTE NAME="File Count">3</ATTRIBUTE>
<ATTRIBUTE NAME="Total Size">139271</ATTRIBUTE>
<ATTRIBUTE NAME="Version">5: 5: 0: 423</ATTRIBUTE>

</OBJECT>
</CLASS>
<IDENTIFICATION>
<ATTRIBUTE NAME="Name">ALTIRISTEST_CPQ</ATTRIBUTE>
<ATTRIBUTE NAME="Domain">FIDDOMRTLSLC</ATTRIBUTE>
<ATTRIBUTE NAME="Altkey1" NULL="FALSE" />
<ATTRIBUTE NAME="Altkey2">00-02-A5-1A-67-A6</ATTRIBUTE>
<ATTRIBUTE NAME="Additional Column">additional column value</ATTRIBUTE>
</IDENTIFICATION>

</ASSET>
</INVENTORY>
</RESOURCE>
<RESOURCE GUID="{C116FCBF-5B94-4F15-BF95-5795DBD384CD}" NAME="ALTIRISTEST_CPQ" 
SOURCE="" SITE_CODE="756win" DOMAIN="FIDD" SYSTEM_TYPE="Win32" 
OS_NAME="Microsoft Windows XP" OS_TYPE="Professional" OS_VERSION="5.1" 
OS_REVISION="Service Pack 1" LAST_LOGON_USER="" LAST_LOGON_DOMAIN="" 
AdditionalCol="Additional Col Value">
</RESOURCE>
</RESOURCE_TYPE>
</AEXDATAEXTRACT>


Current XSL
============

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
version="1.0">
<xsl:output method="text"/>
<xsl:variable name="mostattributes">
<xsl:for-each select="//RESOURCE">
<xsl:sort select="count(attribute::*)"
order="descending" data-type="number"/>
<xsl:if test="position()=1">
<xsl:value-of select="generate-id()"/><!-- this line has changed! -->
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="mostattributes-node" select="//node()[generate-id() = 
$mostattributes]"/>
<xsl:template match="/">
<xsl:for-each select="//node()[generate-id() = $mostattributes]/@*">
<xsl:value-of select="name(.)" ></xsl:value-of>
<xsl:if test="position()!=last()">,</xsl:if>
</xsl:for-each>
<xsl:text>&#10;</xsl:text>
<xsl:apply-templates select="//RESOURCE"/>
</xsl:template>

<xsl:template match="RESOURCE">
<xsl:variable name="r" select="."/>
<xsl:for-each select="$r>
<!-- Here I want to output the value of the attributes for the node with the 
same name as the node having maximum column. I am having the value of maximum 
node in the variable i.e. the unique identifie for that node from which i can 
get the other node. I wish to compare the attribute names here if it matches 
then printing their value otherwise printing separator. Do i have to run two 
for loop or storing value of one node into variable and running for loop for 
the other and comparing attribute values. Please help me to do this.-->
<xsl:if test="position()!=last()">,</xsl:if>
</xsl:for-each>
<xsl:text>&#10;</xsl:text>
</xsl:template>
</xsl:stylesheet>

-------------------------------------------------------------------

Expected output is
==================

DTD_VERSION,EXTRACT_START_DATETIME,EXTRACT_TYPE,EXTRACT_COLLECTION,ParentID,GU
ID,NAME,DESCRIPTION,SOURCE,CREATED_DATE,MODIFIED_DATE,DELETED,ParentID,GUID,NA
MESOURCE,SITE_CODE,DOMAIN,SYSTEM_TYPE,OS_NAME,OS_TYPE,OS_VERSION,OS_REVISION,L
AST_LOGON_USER,LAST_LOGON_DOMAIN,ParentID,SelfID(Inventory),ParentID(Inventory
),SelfID(Asset),TableName,ParentID(Assetid), SelfID(idForTable1 i.e. 
Identification),Name,Domain,Altkey1,Altkey2,ParentID(Asset node is 
parent),SelfID(idForTable2),Agent Name,Product Version,Build Number,Install 
Path,ParentID(Asset node id),SelfID(Self id for table3),Collection Time,File 
Count,Total Size,Version


2.2,1/9/2003,4:49:39 
PM,FULL,,ParentID,{493435f7-3b17-4c4c-b07f-c23e7ab7781f},Computer,Asset Type 
definition for Computer,Asset, 7/16/2002 5:22:23 PM,9/23/2002 2:17:48 
PM,0,ParentId,{C116FCBF-5B94-4F15-BF95-5795DBD384CD},ALTIRISTEST_CPQ,,756win,F
IDD,Win32,Microsoft Windows XP,Professional,5.1,Service Pack 
1,,,ParentID,InventoryID(selfId),ParentID(Inventory 
ID),AssetID(selfid),TableName(Identification),ALTIRISTEST_CPQ,FIDDOMRTLSLC,,00
-02-A5-1A-67-A6

,,,,,,,,,,,,,,,,,,,,,,,,,,,,(Inventory 
ID),AssetID(selfid),TableName(ClientAgent),parentID(AssetID),TableID(obtained 
using generateid()),Altiris eXpress NS Client,5.5.0.517,517,C:\Program 
Files\Altiris\eXpress\NS Client
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,Altiris eXpress Inventory 
Solution,5.5.0.424,424,C:\Program Files\Altiris\eXpress\NS Client\Software 
Delivery\Software Packages\{01B54EB5-3679-4C73-9E10-E169D5A5EC59}
,,,,,,,,,,,,,,,,,,,,,,,,,,,,(Inventory 
ID),AssetID(selfid),TableName(Inventory_Results),,,,,,ParentID(AsseId),TableID
,1/9/2003 3:06:56 AM,3,139271,5: 5: 0: 423

and so on....


--------------------------------------------------------------------

My xml is like it has tables corresponding to nodes in the database.
Each table (node in the xml) has columns in it. The way i get tables from the 
input xml are.
1) Each node is a table. And attributes with it if present are the columns for 
it. (Example : Resource, Resouce_Type in the above example)
2) Node where attributes are not present are still a table with columns like 
primary key and foreign key which i am generating using generate-id function. 
I am relating all the tables like this. (Example, Asset, Inventory etc)
3)There are nodes which has tables and has many records in it.
(Example Class node contains Object node (rows) and Attribute nodes (as 
column).

As per requirement I have to get the column header in the first line of my 
output and column data in the succeeding lines. To sovle this problem. As i 
informed earlier the no. of columns in the table are changing in the input 
xml. So i m trying to find first the tables with maximum column and printing 
them as my header in the first line.

To solve this problem i got the first thing after lot of struggle. I got the 
table name of i.e. node having maximum attributes and printing the header 
(column names). For test purpose i was trying to get data but got stucked. If 
i am getting that I can proceed with further processing.

Now can i find the nodes having maximum child nodes in the same input xml ? 
And storing the unique identifier for it as well ?? This is of type 3 table 
listed above. Can anyone tell me will that be the way or someother way ?

I am trying my best to get the expected output. I appreciate the kind of help 
I am getting for this list and from all the gurus like Mike, David, Wendell 
and all.

Eagerly waiting for reply.

Regards,
Dipesh






Date: Fri, 22 Aug 2003 22:26:39 +0200
From: "Dimitre Novatchev" <dnovatchev@xxxxxxxxx>
Subject: [xsl] Re: How to get nodes with maximum attributes ??

> I am trying to get the node with maximum attributes. How do i get that.
Facing

It is not necessary at all to modify the value of a variable in order to
find the maximum (and then the node with the maximum).

Here are two solutions:

<!-- Solution 1 (O(N)) -->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>

<xsl:template match="/">
<xsl:call-template name="max">
<xsl:with-param name="pNodes" select="/*/*"/>
</xsl:call-template>
</xsl:template>

<xsl:template name="max">
<xsl:param name="pNodes" select="/.."/>
<xsl:variable name="vnumNodes" select="count($pNodes)"/>
<xsl:choose>
<xsl:when test="$vnumNodes &lt; 2">
<xsl:value-of select="count($pNodes[1]/@*)"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="vHalf" select="$vnumNodes div 2"/>
<xsl:variable name="max1">
<xsl:call-template name="max">
<xsl:with-param name="pNodes"
select="$pNodes[position() &lt;= $vHalf]"/>
</xsl:call-template>
</xsl:variable>

<xsl:variable name="max2">
<xsl:call-template name="max">
<xsl:with-param name="pNodes"
select="$pNodes[position() > $vHalf]"/>
</xsl:call-template>
</xsl:variable>

<xsl:value-of select="$max1 * ($max1 >= $max2)
+
$max2 * ($max1 &lt; $max2)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>


<!-- Solution 2 (O(N*log2(N))) but faster in almost all real cases -->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>

<xsl:template match="/">
<xsl:call-template name="max">
<xsl:with-param name="pNodes" select="/*/*"/>
</xsl:call-template>

</xsl:template>

<xsl:template name="max">
<xsl:param name="pNodes" select="/.."/>

<xsl:for-each select="$pNodes">
<xsl:sort select="count(@*)" data-type="number" order="descending"/>
<xsl:if test="position() = 1">
<xsl:value-of select="count(@*)"/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>


When applied on this source.xml:

<t>
<e a1="1" a2="2"/>
<e a1="1" a2="2" a3="3"/>
<e a1="1" a2="2"/>
<e a1="1" a2="2"/>
<e a1="1" a2="2"/>
<e a1="1" a2="2" a3="3" a4="4"/>
<e a1="1" a2="2"/>
<e a1="1" a2="2"/>
<e a1="1" a2="2"/>
<e a1="1" a2="2"/>
</t>

the correct result is produced:
4

One can also find a node (or all nodes) having this maximum value -- either
by modifying the above solutions to return a node (the first solution will
need the xxx:node-set() extension) or simply with this XPath expression:

$allNodes[count(@*) = $maxValueFound]



=====
Cheers,

Dimitre Novatchev.
http://fxsl.sourceforge.net/ -- the home of FXSL


 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list



Current Thread
Keywords