Wikis - Page

XPath Examples

1 Likes

This page is intent to help demystify XPATH expressions by showing example expressions with explanations as to what elements they match. Example traces are encouraged. 

Reference Information

XPath 1.0 http://www.w3.org/TR/xpath

XPath 2.0 http://www.w3.org/TR/xpath20 
Note that XPath 2.0 or higher is not supported by Identity Manager 

XPATH 2.0 Warning also applies to the following links:

http://www.w3schools.com/Xpath/

http://www.zvon.org/xxl/XPathTutorial/General_ger/examples.html

Geoff Carman's series on XPath

 has written extensively regarding Xpath and Identity Manager, here are the most essential articles:

http://www.novell.com/communities/node/4833/some-thoughts-xpath-novell-identity-manager 
http://www.novell.com/communities/node/6175/xpath-and-context-node 
http://www.novell.com/communities/node/6109/xpath-and-math 
http://www.novell.com/communities/node/6179/using-string-compares-xpath-statements 
http://www.novell.com/communities/node/6910/another-attempt-explaining-xpath-context-node 
http://www.novell.com/communities/node/5845/using-xpath-examine-association-values 
http://www.novell.com/communities/node/5686/cool-tricks-using-xpath-nodesets 
http://www.novell.com/communities/node/4825/using-global-configuration-values-xpath 
http://www.novell.com/communities/node/6276/using-xpath-get-position-node-node-set 
http://www.novell.com/communities/node/5818/different-attribute-options-identity-manager 
http://www.novell.com/communities/node/9214/example-walk-through-using-xpath-identity-manager

Others have also contributed valuable articles regarding XPath, here are some key articles:

https://www.netiq.com/communities/cool-solutions/manipulating-node-sets-idm-via-set-operations/

Examples Strip Values Strip Empty Values [Add]

This rule removes elements that have either an empty value [value="] or do not have a value defined [not(*)]

<rule>
  <description>Strip Empty Values [Add]</description>
  <conditions>
    <and>
      <if-operation mode="case" op="equal">add</if-operation>
    </and>
  </conditions>
  <actions>
    <do-strip-xpath expression="add-attr[value="]"/>
    <do-strip-xpath expression="add-attr[not(*)]"/>
  </actions>
</rule>

When the above rule is applied to this input document:

<input>
  <add class-name="User" qualified-src-dn="o=dirXML Test\ou=Users\cn=User1" src-dn="o=dirXML Test\ou=Users\cn=User1">
    <association>o=dirXML Test\ou=Users\cn=User1</association>
    <add-attr attr-name="cn">
      <value>User1</value>
    </add-attr>
    <add-attr attr-name="Surname">
      <value></value>
    </add-attr>
    <add-attr attr-name="Given Name"/>
  </add>
</input>

The result is:

<input>
  <add class-name="User" qualified-src-dn="o=dirXML Test\ou=Users\cn=User1" src-dn="o=dirXML Test\ou=Users\cn=User1">
    <association>o=dirXML Test\ou=Users\cn=User1</association>
    <add-attr attr-name="cn">
      <value>User1</value>
    </add-attr>
  </add>
</input>

The expression value=" matches the <value></value> and the expression not(*) matches the Given Name example above

Strip Empty Values [Modify]

This is similar to the above rule, but looks for elements that are modify values as opposed to add values.

<rule>
  <description>Strip Empty Values [Modify]</description>
  <conditions>
    <or>
      <if-operation mode="case" op="equal">modify</if-operation>
      <if-operation mode="case" op="equal">sync</if-operation>
    </or>
  </conditions>
  <actions>
    <do-strip-xpath expression="modify-attr/add-value[value="]"/>
    <do-strip-xpath expression="modify-attr[not(*)]"/>
  </actions>
</rule>

Example Document that would have the value stripped:

<modify-attr attr-name="Surname">
  <add-value>
    <value></value>
  </add-value>
</modify-attr>

OR

<modify-attr attr-name="Surname"/>
Strip 'Remove All Values'

This removes the instance of <remove-all-values/> from an attribute named Surname

<do-strip-xpath expression="*[@attr-name='Surname']/remove-all-values"/>

If the above rule was applied to this document:

<modify-attr attr-name="Surname">
  <remove-all-values/>
  <add-value>
    <value>Last Name1</value>
  </add-value>
</modify-attr>

The result would be:

<modify-attr attr-name="Surname">
  <add-value>
    <value>Last Name1</value>
  </add-value>
</modify-attr>
Strip a Specific Value of a Specific Attribute
<do-strip-xpath expression="*[@attr-name='CN']/add-value[value='Common Name']"/>

Removes this Value of Common Name:

<add-attr attr-name="cn">
  <value>Common Name</value>
</add-attr>

Which Returns:

<add-attr attr-name="cn"/>

You should then follow with the strip empty values rules above to clean this up.

After using the Policy Simulator in Designer I found that the following XPath expressions may work better

<do-strip-xpath expression="*[@attr-name='cn'][value='Common Name']/value"/>

Removes this Value of Common Name:

<input>
  <add class-name="User">
    <add-attr attr-name="cn">
      <value type="string">Common Name</value>
    </add-attr>
  </add>
 </input>

Which Returns:

<input>
  <add class-name="User">
    <add-attr attr-name="cn"/>
  </add>
</input>

And to clean it all up in one step...

<do-strip-xpath expression="*[@attr-name='cn'][value='Common Name']"/>

Removes the entire CN add-attr:

<input>
  <add class-name="User">
    <add-attr attr-name="cn">
      <value type="string">Common Name</value>
    </add-attr>
  </add>
 </input>

Which Returns:

<input>
  <add class-name="User">
</input>
Strip All Empty Nodes (unscoped)

Author: Lothar Haeger  

Strip empty nodes of all kinds of operations that usually pass event/command policies (not scoped to add or modify)

<rule>
   <description>Strip Empty Nodes</description>
   <conditions/>
   <actions>
      <do-strip-xpath expression='self::instance/attr/value[not(*)][not(text()) or text()=""]'/>
      <do-strip-xpath expression='self::instance/attr[not(*)]'/>
      <do-strip-xpath expression='self::add/add-attr/value[not(*)][not(text()) or text()=""]'/>
      <do-strip-xpath expression='self::add/add-attr[not(*)]'/>
      <do-strip-xpath expression='self::modify/modify-attr/remove-value/value[not(*)][not(text()) or text()=""]'/>
      <do-strip-xpath expression='self::modify/modify-attr/remove-value[not(*)]'/>
      <do-strip-xpath expression='self::modify/modify-attr/add-value/value[not(*)][not(text()) or text()=""]'/>
      <do-strip-xpath expression='self::modify/modify-attr/add-value[not(*)]'/>
      <do-strip-xpath expression='self::modify/modify-attr[not(*)]'/>
   </actions>
</rule>

No need to set conditions here, they're kind of included in the xpath statements as they won't do anything if they do not match.

Or even shorter in a new 2014 version:

<rule>
	<description>Strip Empty Nodes</description>
	<conditions>
		<or>
			<if-operation mode="regex" op="equal">add|modify|instance</if-operation>
		</or>
	</conditions>
	<actions>
		<do-strip-xpath expression='.//value[not(*) and (not(text()) or text()="")]'/>
		<do-strip-xpath expression='self::modify/modify-attr/remove-value[not(*)]'/>
		<do-strip-xpath expression='self::modify/modify-attr/add-value[not(*)]'/>
		<do-strip-xpath expression='*[@attr-name and not(*)]'/>
	</actions>
</rule>

Note: in this new version you must keep the condition block, otherwise the last do-strip-xpath might do funny things during driver startup...

String Operations Retrieve a Portion of a String

This example grabs just the last portion of a string delimited by a hyphen

<actions>
  <do-set-local-variable name="vSSN" scope="policy">
    <arg-node-set>
      <token-split delimiter="-">
        <token-local-variable name="current-node"/>
      </token-split>
    </arg-node-set>
  </do-set-local-variable>
  <do-set-dest-attr-value name="vLAST4">
    <arg-value>
      <token-xpath expression="$vSSN[3]"/>
    </arg-value>
  </do-set-dest-attr-value>
</actions>

When applied to the value 333-22-4444 this would return just 4444

Remove Leading Zeros from a Number

When presented with a value that is padded with one or more zeros such as 0003456 or 001 you want to have the actual number passed through as a string for a result of 3456 or 1

<actions>
     <do-reformat-op-attr name="jobCode">
          <arg-value type="string">
               <token-xpath expression="number($current-value)"/>
          </arg-value>
     </do-reformat-op-attr>
</actions>
Working with nodesets Get Value From a Nodeset

A query noun token returns its values in type nodeset. One way to get a specific value is to set a local variable using the query token and then use an xpath expression to return the desired value.

<token-xpath expression="$MyLocalVar//value[1]/text()"/>

The [1] indicates the first value node inside your variable. See below if your order is unpredictable and you might need a specific value.

Get Text of Specific Value From a Nodeset

You want to test for the presence of one or more of several values. If found you want the found value returned.

For Example:

A user is assigned to three buildings.  If one of those buildings is in a specific list of buildings you want to know which of those specific ones.

building: Houston
building: Paris
building: New York

Determine if a user has a building value of Athens, Paris, Sydney, or London

First, set a nodeset local variable equal to the attribute value:

<do-set-local-variable name="lv-buildings" scope="policy">
	<arg-node-set>
		<token-attr name="buildings"/>
	</arg-node-set>
</do-set-local-variable>

Next, the xpath that will return the desired value:

<token-xpath expression="$lv-buildings[text()='Athens' or text()='Paris' or text()='Sydney' or text()='London']/text()"/>

This expression returns the string 'Paris'

Note: this expression works when you expect only a single value to return. If the user has more than one value that matches you end up with a concatenated string. How do we fix that? If you set another nodeset variable equal to the xpath expression and remove the trailing text() element then you will get the nodes that match and can iterate through them or handle them as you please. You might also consider using a strip XPATH expression. There are several approaches to handle that.

Min and max value in a nodeset

Author: Lothar Haeger  

For more details visit:

https://community.microfocus.com/cyberres/b/sws-22/posts/again-the-power-of-xpath-or-the-missing-max-function

<do-set-local-variable name="minValue" scope="policy">
   <arg-string>
      <token-xpath expression='self::*//value[ancestor::*/@attr-name="attrNameGoesHere"][not(. > preceding-sibling::value or . > following-sibling::value)][1]'/>
   </arg-string>
</do-set-local-variable>
<do-set-local-variable name="maxValue" scope="policy">
   <arg-string>
      <token-xpath expression='self::*//value[ancestor::*/@attr-name="attrNameGoesHere"][not(. < preceding-sibling::value or . < following-sibling::value)][1]'/>
   </arg-string>
</do-set-local-variable>
Add an Element to a Nodeset

This example inserts a remove-all-values element into a modify of an existing attribute. This would be used to ensure that a SET destination attribute action always takes place instead of an add destination attribute.

<do-append-xml-element before="add-value" expression="modify-attr[@attr-name='Surname']" name="remove-all-values"/>

The important items to note here are as follows:

  1. The expression value selects the node that contains a modify of the Surname attribute
  2. The before value ensures that the remove values element is above the add in the document and processed first. Note that it is specified in the context of the current node which is now the values selected by the XPATH expression.

Here is an example input document:

<input>
	<modify class-name="User" qualified-src-dn="o=dirXML Test\ou=Users\cn=User1">
		<association>o=dirXML Test\ou=Users\cn=User1</association>
		<modify-attr attr-name="Surname">
			<add-value>
				<value type="string">Last Name1</value>
			</add-value>
		</modify-attr>
	</modify>
</input>

The following rule would test to ensure that Surname is present and that it doesn't already contain a remove all values element:

<rule>
	<description>Single Valued Attribute Handler</description>
	<conditions>
		<and>
			<if-class-name mode="nocase" op="equal">User</if-class-name>
		</and>
	</conditions>
	<actions>
		<do-if>
			<arg-conditions>
				<and>
					<if-xpath op="not-true">modify-attr[@attr-name='Surname']/remove-all-values</if-xpath>
					<if-op-attr name="Surname" op="available"/>
				</and>
			</arg-conditions>
			<arg-actions>
				<do-append-xml-element before="add-value" expression="modify-attr[@attr-name='Surname']" name="remove-all-values"/>
			</arg-actions>
			<arg-actions/>
		</do-if>
	</actions>
</rule>

This would be the resultant document:

<input>
	<modify class-name="User" qualified-src-dn="o=dirXML Test\ou=Users\cn=User1">
		<association>o=dirXML Test\ou=Users\cn=User1</association>
		<modify-attr attr-name="Surname">
			<remove-all-values/>
			<add-value>
				<value type="string">Last Name1</value>
			</add-value>
		</modify-attr>
	</modify>
</input>
Working with Nodeset Attributes Remove an attribute of an element

From: Father Ramon \ Forums

This expression will strip an attribute of a value.

To remove the "type" attribute of the value below:

<attr attr-name="memberOf">
     <value naming="true" type="dn">CN=dept,OU=groups,DC=domain,DC=com</value>
</attr>

Apply this expression:

<do-strip-xpath expression="*[@attr-name="memberOf"]//value/@type"/>

Which would return this:

<attr attr-name="memberOf">
     <value naming="true">CN=dept,OU=groups,DC=domain,DC=com</value>
</attr>

This could also be accomplished without XPATH as below:

<do-reformat-op-attr name="memberOf">
     <arg-value type="string">
          <token-local-variable name="current-value"/>
     </arg-value>
</do-reformat-op-attr>

From Father Ramon:

Reformat actually replaces all the existing values for the attributes with new ones. In that sense it is much more broad than the XPath approach because it actually throws away the original value elements and replaces it with a newly fabricated one that will only have on it exactly what you specify to put on it.

Parse a CN from an attribute of an element

From: Father Ramon \ Forums

This rule will parse the CN from an attribute of old-src-dn in a rename operation.

Input Document:

<input>
   <rename class-name="User" old-src-dn="\TREE\organization\myolduid" src-dn="\TREE\organization\mynewuid">
      <association state="associated">\TREE\organization\mynewuid</association>
      <new-name>mynewuid</new-name>
   </rename>
</input>

Policy:

<do-set-local-variable name="old-cn">
   <arg-string>
      <token-parse-dn start="-1" length="1" src-dn-format="slash" dest-dn-format="slash">
         <token-xpath expression="@old-src-dn"/>
      </token-parse-dn>
   </arg-string>
</do-set-local-variable>

 

Structured Attributes Component Value of a Structured Attribute (By Name)

This rule will grab the value of a single component of a structured attribute using the component's name. This can be used with attributes like postal address.

<token-xpath expression="$current-node/component[@name='COMPONENT1']"/>

When applied to this example:

<modify-attr attr-name="Postal Address">
  <remove-all-values/>
  <add-value>
    <value type="structured">
      <component name="COMPONENT1">Address Name</component>
      <component name="COMPONENT2">1234 Street Address</component>
      <component name="COMPONENT3">Additional Street Address</component>
      <component name="COMPONENT4">City</component>
      <component name="COMPONENT5">State</component>
      <component name="COMPONENT6">84000</component>
    </value>
  </add-value>						
</modify-attr>

The value Address Name would be returned.

Component Value of a Structured Attribute (By Position)

This rule will grab the value of a single component of a structured attribute by it's position in the attribute. This can be used with attributes like postal address, where the components are not named.

<token-xpath expression="$current-node/component[2]"/>

When applied to this example:

     <modify-attr attr-name="homePostalAddress">
       <remove-all-values/>
       <add-value>
         <value>
           <component type="string">Herman Munster</component>
           <component type="string">1313 Mockingbird Lane</component>
           <component type="string">" "</component>
           <component type="string">Mockingbird Heights</component>
           <component type="string">MN</component>
           <component type="string">12345</component>
         </value>
       </add-value>
     </modify-attr>

The value "1313 Mockingbird Lane" will be returned.

Working With Event and Status Messages
Detect a Warning Error

This expression detects a warning returned on the driver.

<if-xpath op="true">self::status[@level = 'warning']</if-xpath>

For example:

<input>
  <status event-id="777" level="warning">Operation vetoed by unassociated object.</status>
</input>

The subsequent rule:

<token-xpath expression="self::status"/>

Would return "Operation vetoed by unassociated object."

Detect a Merge Event
<conditions>
  <and>
    <if-operation op="equal">modify</if-operation>
    <if-xpath op="true">@from-merge = 'true'</if-xpath>
  </and>
</conditions>
Remove Un-Associated Group Memberships

You want to remove values of an attribute that do not contain a particular XML attribute

Input Document

	<input>
		<modify class-name="User" qualified-src-dn="o=dirXML Test\ou=Users\cn=User1">
			<association>o=dirXML Test\ou=Users\cn=User1</association>
			<modify-attr attr-name="Surname">
				<remove-all-values/>
				<add-value>
					<value>Last Name1</value>
				</add-value>
			</modify-attr>
			<modify-attr attr-name="Given Name">
				<remove-all-values/>
				<add-value>
					<value>First Name1</value>
				</add-value>
			</modify-attr>
			<modify-attr attr-name="Group Membership">
				<add-value>
					<value association-ref="cn=groupname,o=myorg" type="dn">\Tree1\myorg\groupname</value>
					<value type="dn">\Tree1\myorg\anothergroup</value>
					<value association-ref="cn=yetonemore" type="dn">\Tree1\myorg\yetonemore</value>
				</add-value>
			</modify-attr>
		</modify>
	</input>

Policy

	<rule>
		<description>Strip Unassociated Memberships</description>
		<conditions>
			<and>
				<if-op-attr name="Group Membership" op="available"/>
			</and>
		</conditions>
		<actions>
			<do-strip-xpath expression="*[@attr-name='Group Membership']/add-value/value[not(@association-ref)]"/>
		</actions>
	</rule>

Output Result

	<input>
		<modify class-name="User" qualified-src-dn="o=dirXML Test\ou=Users\cn=User1">
			<association>o=dirXML Test\ou=Users\cn=User1</association>
			<modify-attr attr-name="Surname">
				<remove-all-values/>
				<add-value>
					<value>Last Name1</value>
				</add-value>
			</modify-attr>
			<modify-attr attr-name="Given Name">
				<remove-all-values/>
				<add-value>
					<value>First Name1</value>
				</add-value>
			</modify-attr>
			<modify-attr attr-name="Group Membership">
				<add-value>
					<value association-ref="cn=groupname,o=myorg" type="dn">\Tree1\myorg\groupname</value>
					<value association-ref="cn=yetonemore" type="dn">\Tree1\myorg\yetonemore</value>
				</add-value>
			</modify-attr>
		</modify>
	</input>

 

Query Vault for DN using Email address as a matching attribute
<rule>
	<description>Query Manager DN</description>
	<conditions>
		<and>
			<if-attr name="directSupervisorEmail" op="available"/>
		</and>
	</conditions>
	<actions>
		<do-set-local-variable name="DEST-DN" scope="policy">
			<arg-string>
				<token-text xml:space="preserve">rackspace\users\active</token-text>
			</arg-string>
		</do-set-local-variable>
		<do-set-local-variable name="SEARCH-VALUE" scope="policy">
			<arg-string>
				<token-attr name="directSupervisorEmail"/>
			</arg-string>
		</do-set-local-variable>
		<do-set-local-variable name="ManagerDN" scope="policy">
			<arg-node-set>
				<token-xpath expression="query:search($destQueryProcessor,
					'subtree',,$DEST-DN,,'Internet Email  Address',$SEARCH-VALUE,)"/>
			</arg-node-set>
		</do-set-local-variable>
		<do-set-local-variable name="SRC-DN" scope="policy">
			<arg-string>
				<token-xpath expression="$ManagerDN/@src-dn"/>
			</arg-string>
		</do-set-local-variable>
	</actions>
</rule>
<rule>
	<description>Display Direct Supervisor Query Result</description>
	<conditions>
		<and>
			<if-local-variable name="SRC-DN" op="available"/>
		</and>
	</conditions>
	<actions>
		<do-set-local-variable name="lvarManagerDN" scope="policy">
			<arg-string>
				<token-parse-dn dest-dn-format="src-dn" start="0">
					<token-local-variable name="SRC-DN"/>
				</token-parse-dn>
			</arg-string>
		</do-set-local-variable>
		<do-set-dest-attr-value name="manager">
			<arg-value>
				<token-local-variable name="lvarManagerDN"/>
			</arg-value>
		</do-set-dest-attr-value>
	</actions>
</rule>
Run external code on the IDM engine server

Windows only 

<rule>
	<description>Start Notepad</description>
	<conditions/>
	<actions>
		<do-set-local-variable name="Runtime">
			<arg-object>
				<token-xpath expression="java.lang.Runtime:getRuntime()"/>
			</arg-object>
		</do-set-local-variable>
		<do-set-local-variable name="Notepad">
			<arg-object>
				<token-xpath expression="java.lang.Runtime:exec($Runtime, 'notepad.exe c:\NewDoc.txt')"/>
			</arg-object>
		</do-set-local-variable>
		<do-set-local-variable name="Returncode">
			<arg-string>
				<token-xpath expression="java.lang.Process:waitFor($Notepad)"/>
			</arg-string>
		</do-set-local-variable>
		<do-set-local-variable name="Returncode">
			<arg-string>
				<token-xpath expression="java.lang.Process:exitValue($Notepad)"/>
			</arg-string>
		</do-set-local-variable>
	</actions>
</rule>

(with IDM versions earlier than 3.5 you need to define namespace for the java classes)

Allow Unsynchronized Values to Exist

When an attribute is synchronized, you usually designate one system as authoritative. However, their could be instances where you want both to be authoritative. For example:

  • Identity Vault Values
    • Today
    • Tomorrow
  • Destination System Values
    • Yesterday
<rule>
	<description>Merge Description Values</description>
	<comment xml:space="preserve">This rule will take any description values being sync'd and convert
	   them to add value events instead of synchronization to preserve destination values.</comment>
	<conditions>
		<and>
			<if-op-attr name="Description" op="available"/>
		</and>
	</conditions>
	<actions>
		<do-set-local-variable name="lvDescription" scope="policy">
			<arg-node-set>
				<token-op-attr name="Description"/>
			</arg-node-set>
		</do-set-local-variable>
		<do-set-local-variable name="lvDescriptionRemove" scope="policy">
			<arg-node-set>
				<token-xpath expression="*[@attr-name='Description']/remove-value/value"/>
			</arg-node-set>
		</do-set-local-variable>
		<do-strip-op-attr name="Description"/>
		<do-for-each>
			<arg-node-set>
				<token-local-variable name="lvDescriptionRemove"/>
			</arg-node-set>
			<arg-actions>
				<do-remove-dest-attr-value name="Description">
					<arg-value>
						<token-local-variable name="current-node"/>
					</arg-value>
				</do-remove-dest-attr-value>
			</arg-actions>
		</do-for-each>
		<do-for-each>
			<arg-node-set>
				<token-local-variable name="lvDescription"/>
			</arg-node-set>
			<arg-actions>
				<do-add-dest-attr-value name="Description">
					<arg-value>
						<token-local-variable name="current-node"/>
					</arg-value>
				</do-add-dest-attr-value>
			</arg-actions>
		</do-for-each>
	</actions>
</rule>

Resultant Values:

    • Today
    • Tomorrow
    • Yesterday

Furthermore, removing a value that only exists in the vault, will simply remove that value in the destination.

Labels:

How To-Best Practice
Comment List
Related
Recommended