I am new to XSLT Please help, Cannot use XSLT 2.0


The group priority level

lowest(Public-1) TO Highest (PRIVATE-3)

Public-1 Public-2 Public-3 CONFID-1 CONFID-2 CONFID-3 PRIVATE-1 PRIVATE-2 PRIVATE-3

For example a user LDAP query gives a file

<result>
<DN>CN=Test User ID Priv-2,CN=Users,DC=Lab,DC=acme,DC=com</DN>
<attribute-value name="memberOf">CN=CONFID-2,OU=Groups,DC=Lab,DC=acme,DC=com</attribute-value>
<attribute-value name="memberOf">CN=PRIVATE-2,OU=Groups,DC=Lab,DC=acme,DC=com</attribute-value>
<attribute-value name="memberOf">CN=Public-3,OU=Groups,DC=Lab,DC=acme,DC=com</attribute-value>
</result>

The xslt should select the highest priority level available for the user and output it

In the file above the user has membership in groups

CONFID-2, PRIVATE-2, Public-3


the xslt should give the output just

CN=PRIVATE-2,OU=Groups,DC=Lab,DC=acme,DC=com

Thanks in advance for your help. I know the procedure to follow.

- gather the all groups available for the user.

- translate/map the gathered groups into numericals

- select the highest

I am very new to XSLT and am trying for 2 days. I just need a small code snippet of translating/mapping the group names to numericals and to select the highest numerical. I will never ask you a working stylesheet.

Recommended Answers

All 17 Replies

I'm working on a solution for this on and off as the day goes on, I'll let you know. It's actually a reasonably complicated problem in XSLT, but I'll have something quick and dirty today :)

Alrighty, I have a quick and dirty solution. It's not pretty and given some time I could probably improve it, but it should give you an idea of what you need to do.

The idea of the transformation is as follows. There is a variable that I've defined at the top called "grouplevel". This is basically a look up table that matches your LDAP levels with a number priority from lowest to highest.

Next things that I do is create a temporary tree called "getlevels". For each "attribute-value" node it creates a "setlevel" element. This temp tree takes each "attribute-value" node and locates the LDAP code that's in the string ("getCN"). Then it uses the lookup table to find the number priority that that particular LDAP code has and creates a "priority" attribute in the "setlevel" element.

Once I have all the priorities marked for each setlevel, I then use that temporary tree and sort it from highest to lowest. This is done using a second temporary variable called "sortlevels".

Once I have a node set that contains the priority levels and it's sorted from highest to lowest, I can just select the FIRST node in that set. The first one will always have the highest priority over the rest of them. If you have situations where a user can have the SAME priority twice, it will run into problems. All the code is below and tested a few times.

Input Docmuent

<result>
	<DN>CN=Test User ID Priv-2,CN=Users,DC=Lab,DC=acme,DC=com</DN>
	<attribute-value name="memberOf">CN=CONFID-2,OU=Groups,DC=Lab,DC=acme,DC=com</attribute-value>
	<attribute-value name="memberOf">CN=PRIVATE-2,OU=Groups,DC=Lab,DC=acme,DC=com</attribute-value>
	<attribute-value name="memberOf">CN=Public-3,OU=Groups,DC=Lab,DC=acme,DC=com</attribute-value>
</result>

XSLT

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

	<xsl:variable name="grouplevel">
		<levels>
			<level set="1">Public-1</level>
			<level set="2">Public-2</level>
			<level set="3">Public-3</level>
			<level set="4">CONFID-1</level>
			<level set="5">CONFID-2</level>
			<level set="6">CONFID-3</level>
			<level set="7">PRIVATE-1</level>
			<level set="8">PRIVATE-2</level>
			<level set="9">PRIVATE-3</level>
		</levels>
	</xsl:variable>

	<xsl:template match="/">
		<xsl:variable name="getlevels" >
			<xsl:apply-templates select="result/attribute-value" />
		</xsl:variable>		
		<xsl:variable name="sortlevels" >
			<xsl:apply-templates select="$getlevels/setlevel" >
				<xsl:sort select="@priority" order="descending" />
			</xsl:apply-templates>
		</xsl:variable>
		<xsl:value-of select="$sortlevels/setlevel[1]" />
	</xsl:template>

	<xsl:template match="attribute-value" >
		<setlevel>
			<xsl:variable name="getCN" select="substring-before(substring-after(., 'CN='),',')"/>
			<xsl:attribute name="priority">
				<xsl:value-of select="$grouplevel//level[. = $getCN]/@set" />
			</xsl:attribute>
			<xsl:value-of select="." />
		</setlevel>
	</xsl:template>

	<xsl:template match="setlevel" >
		<xsl:copy-of select="." />
	</xsl:template>

</xsl:stylesheet>

Output

CN=PRIVATE-2,OU=Groups,DC=Lab,DC=acme,DC=com

I'm working on a solution for this on and off as the day goes on, I'll let you know. It's actually a reasonably complicated problem in XSLT, but I'll have something quick and dirty today :)

Thank You so-much.

I have reached a stage (after a very dirty stylesheet). But I dont mind as I am stuck with XSLT 1.0 I have to go through

I got the intermediate output after following the procedure suggested by an author of an XSLT book, Using the substring-before and substring-after by cutting the group names to such as

<att>TE-1</att>
<att>C-3</att>
<att>D-2</att>

the rest-of the problem remains
- sorting on this node-set
- and selecting the highest priority group

in the same stylesheet.

That's what temporary variables are good for. Store your first result in a variable like I did, then you can apply an second set of template on that variable to do something else. You can do this continuously actually. Look at what I've done with the "getlevels" and "sortlevels" variables as I've used them to create temporary objects that I can reuse in the same XSLT.

(Or just use my code) :)

I really appreciate your help . Which tool are you using to debug this XSLT mine gives me an error shown below

XslTransformException
---------------------
To use a result tree fragment in a path expression, first convert it to a node-set using the msxsl:node-set() function"

I'm sorry about that. I should've asked what processor you were using :) Obviously you're using a MSXML processor. In order to use the node fragment variables as nodes, they need to be converted to a node set. This is done through the process specific funcitons msxml:node-set(). Here's the modified XSLT that should work for you. Notice the namespace delcaration at the top and the new places where the function has been used. Ran and tested in Stylus Studio on 3 versions of the MSXML processor.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
	<xsl:output indent="yes" method="xml"/>

	<xsl:variable name="grouplevel">
		<levels>
			<level set="1">Public-1</level>
			<level set="2">Public-2</level>
			<level set="3">Public-3</level>
			<level set="4">CONFID-1</level>
			<level set="5">CONFID-2</level>
			<level set="6">CONFID-3</level>
			<level set="7">PRIVATE-1</level>
			<level set="8">PRIVATE-2</level>
			<level set="9">PRIVATE-3</level>
		</levels>
	</xsl:variable>

	<xsl:template match="/">
		<xsl:variable name="getlevels" >
			<xsl:apply-templates select="result/attribute-value" />
		</xsl:variable>		
		<xsl:variable name="sortlevels" >
			<xsl:apply-templates select="msxsl:node-set($getlevels)/setlevel" >
				<xsl:sort select="@priority" order="descending" />
			</xsl:apply-templates>
		</xsl:variable>
		<xsl:value-of select="msxsl:node-set($sortlevels)/setlevel[1]" />
	</xsl:template>

	<xsl:template match="attribute-value" >
		<setlevel>
			<xsl:variable name="getCN" select="substring-before(substring-after(., 'CN='),',')"/>
			<xsl:attribute name="priority">
				<xsl:value-of select="msxsl:node-set($grouplevel)//level[. = $getCN]/@set" />
			</xsl:attribute>
			<xsl:value-of select="." />
		</setlevel>
	</xsl:template>

	<xsl:template match="setlevel" >
		<xsl:copy-of select="." />
	</xsl:template>

</xsl:stylesheet>

However, this does LOCK your transformation to only function on the MSXML processor. If you'd like to be processor agnostic, look into the EXSLT library. it's a set of extension functions that can be used in any transformation if you include the right things into your XSLT. Links below.

http://www.exslt.org/exsl/functions/node-set/index.html

I can't thank you enough Andrews. I used exslt:node-set()according to your suggestion. It worked like charm. I really appreciate your enthusiasm to help people like me who are newbies to XSLT.

This is stands resolved.The code works as intended.

Regards
Bab

Good good :) Glad I could help out. I really recommend getting a good desktop reference book. Michael Kay's WROX book is my personal favorite. Stinks you're limited to XSLT 1.0, but most people are :( If you have any more specific problems, let me know or PM me.

Yah for now have to use 1.0. Thanks Andrews for the offer of help. Really humbled by the gesture. This motivates me to help someone else where I am good at.

Hey Andrews, I have question regarding the output this is producing
the current out put is:
CN=PRIVATE-2,OU=Groups,DC=Lab,DC=acme,DC=com

The desired output

PRIVATE-2 that too I want to get this into a variable at the end to use it below in the stylesheet

I tried

variable=x

value-of substring-before(....)

like you did in the file But unsuccessful in that

Please advice

thanks

Not sure exactly what you mean. Are you saying that you want the desired output to be just a string that contains the highest level for a user? In other words you don't want the output to be the string "CN=PRIVATE-2,OU=Groups,DC=Lab,DC=acme,DC=com", you just want it to be "PRIVATE-2" ?

I was able to do it by modifying the attribute-value template, Now my output is PRIVATE-2

<xsl:template match="attribute-value" >

<setlevel>

<xsl:variable name="getCN" select="substring-before(substring-after(., 'CN=WFM-'),',')"/>

<xsl:attribute name="priority">

<xsl:value-of select="exslt:node-set($grouplevel)//level[. = $getCN]/@set" />

</xsl:attribute>

<xsl:value-of select="substring-before(substring-after(., 'CN=WFM-'),',')" />

</setlevel>


</xsl:template>

my new problem is how to get this PRIVATE-2 into a variable to be used below in the same stylesheet

Thank You

I see. Well it's really hard to guess at what you're trying to do, but it sounds like to me you need to use a parameter, not a variable. The idea is that you can take the string result and PASS it between templates. Look into parameters and how they're used. (xsl:with-param)

You could do something like this.

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

	<xsl:template match="/">
		<xsl:variable name="getlevels">
			<xsl:apply-templates select="result/attribute-value"/>
		</xsl:variable>
		<xsl:variable name="sortlevels">
			<xsl:apply-templates select="msxsl:node-set($getlevels)/setlevel">
				<xsl:sort select="@priority" order="descending"/>
			</xsl:apply-templates>
		</xsl:variable>
		
		<xsl:apply-templates select="SOEMTHING" >
			<xsl:with-param name="NAME" select="msxsl:node-set($sortlevels)/setlevel[1]"/>
		</xsl:apply-templates>

	</xsl:template>

	<xsl:template match="SOMETIHNG" >
		<xsl:param name="NAME" />
		CODE TO DO SOEMTHING
		Now this parameter $NAME is available to use in thie template.
	</xsl:template>

My input file

<result>
  <DN>CN=PrPuConf Test User,OU=Process IDs,OU=Service Accounts,DC=Lab,DC=acme,DC=com</DN>
  <attribute-value name="memberOf">CN=WFM-CONFED-1,OU=Groups,DC=Lab,DC=acme,DC=com</attribute-value>
  <attribute-value name="memberOf">CN=WFM-PRIVATE-2,OU=Groups,DC=Lab,DC=acme,DC=com</attribute-value>
  <attribute-value name="memberOf">CN=WFM-PUBLIC-3,OU=Groups,DC=Lab,DC=acme,DC=com</attribute-value>
</result>

My stylesheet is

<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:exslt="http://exslt.org/common"
                xmlns:dp="http://www.datapower.com/extensions" 
                extension-element-prefixes="dp" 
                exclude-result-prefixes="dp">

  <xsl:output indent="yes" method="text"/>

  <xsl:variable name="grouplevel">

    <levels>

      <level set="1">PUBLIC-1</level>

      <level set="2">PUBLIC-2</level>

      <level set="3">PUBLIC-3</level>

      <level set="4">CONFED-1</level>

      <level set="5">CONFED-2</level>

      <level set="6">CONFED-3</level>

      <level set="7">PRIVATE-1</level>

      <level set="8">PRIVATE-2</level>

      <level set="9">PRIVATE-3</level>

    </levels>

  </xsl:variable>

  <xsl:template match="/">

    <xsl:variable name="getlevels" >

      <xsl:apply-templates select="result/attribute-value" />

    </xsl:variable>

    <xsl:variable name="sortlevels" >

      <xsl:apply-templates select="exslt:node-set($getlevels)/setlevel" >

        <xsl:sort select="@priority" order="descending" />

      </xsl:apply-templates>

    </xsl:variable>
    
    <xsl:apply-templates select="finrole" >
 
      <xsl:with-param name="NAME" select="exslt:node-set($sortlevels)/setlevel[1]"/>
   
    </xsl:apply-templates> 

  </xsl:template>

  <xsl:template match="attribute-value" >

    <setlevel>
      
      <xsl:variable name="getCN" select="substring-before(substring-after(., 'CN=WFM-'),',')"/>

      <xsl:attribute name="priority">

        <xsl:value-of select="exslt:node-set($grouplevel)//level[. = $getCN]/@set" />

      </xsl:attribute>

      <xsl:value-of select="substring-before(substring-after(., 'CN=WFM-'),',')" />

    </setlevel>
    

  </xsl:template>


  <xsl:template match ="setlevel"  >

    <xsl:copy-of  select="."/>
  
  </xsl:template>

  <xsl:template match ="finalrole">
 [B]   ***************need that output to go into the $role below****************[/B] 
    <xsl:choose>
      <xsl:when test="$role = 'PRIVATE-1'">

        <dp:set-variable name="'var://context/response/displayFilter'" value="'local:///XSLT/Private_1_Filter.xsl'"/>

      </xsl:when>
      <!-- If the role is Private 2-->
      <xsl:when test="$role = 'PRIVATE-2'">

        <dp:set-variable name="'var://context/response/displayFilter'" value="'local:///XSLT/Private_2_Filter.xsl'"/>

      </xsl:when>
        </xsl:choose>

      </xsl:template>
  
  
</xsl:stylesheet>

The objective is to set the dp variable using role

Thank You for your help

Here ya go! Examine my comments closely in the code. Note the use of a call template and that I'm PASSING the value of that you want as a parameter into that template.

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exslt="http://exslt.org/common" xmlns:dp="http://www.datapower.com/extensions" extension-element-prefixes="dp" exclude-result-prefixes="dp">
	<xsl:output indent="yes" method="text"/>

	<xsl:variable name="grouplevel">
		<levels>
			<level set="1">PUBLIC-1</level>
			<level set="2">PUBLIC-2</level>
			<level set="3">PUBLIC-3</level>
			<level set="4">CONFED-1</level>
			<level set="5">CONFED-2</level>
			<level set="6">CONFED-3</level>
			<level set="7">PRIVATE-1</level>
			<level set="8">PRIVATE-2</level>
			<level set="9">PRIVATE-3</level>
		</levels>
	</xsl:variable>

	<xsl:template match="/">
		<xsl:variable name="getlevels">
			<xsl:apply-templates select="result/attribute-value"/>
		</xsl:variable>
		<xsl:variable name="sortlevels">
			<xsl:apply-templates select="exslt:node-set($getlevels)/setlevel">
				<xsl:sort select="@priority" order="descending"/>
			</xsl:apply-templates>
		</xsl:variable>
		<xsl:call-template name="finalrole">
			<xsl:with-param name="getCNLevel" select="string(exslt:node-set($sortlevels)/setlevel[1])"/>
		</xsl:call-template>
	</xsl:template>

	<xsl:template match="attribute-value">
		<setlevel>
			<xsl:variable name="getCN" select="substring-before(substring-after(., 'CN=WFM-'),',')"/>
			<xsl:attribute name="priority">
				<xsl:value-of select="exslt:node-set($grouplevel)//level[. = $getCN]/@set"/>
			</xsl:attribute>
			<xsl:value-of select="substring-before(substring-after(., 'CN=WFM-'),',')"/>
		</setlevel>
	</xsl:template>

	<xsl:template match="setlevel">
		<xsl:copy-of select="."/>
	</xsl:template>

	<xsl:template name="finalrole">
		<xsl:param name="getCNLevel" select="/.." />

		<!-- Verified Output you can remove this line -->
		<xsl:value-of select="$getCNLevel" />

		<!-- DO STUFF -->

		<!--<xsl:choose>
			<xsl:when test="$getCNLevel = 'PRIVATE-1'">
				<dp:set-variable name="'var://context/response/displayFilter'" value="'local:///XSLT/Private_1_Filter.xsl'"/>
			</xsl:when>			 
			<xsl:when test="$getCNLevel = 'PRIVATE-2'">
				<dp:set-variable name="'var://context/response/displayFilter'" value="'local:///XSLT/Private_2_Filter.xsl'"/>
			</xsl:when>
		</xsl:choose>-->
	</xsl:template>

</xsl:stylesheet>

Thank you so much for taking time to help Andrews. I will update you on this

Sorry for the delayed reply system was down. They got it up and running today. the method works like a charm.. thank you so-much Andrews

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.