XPath by example in Oracle Service Bus

Oracle Service Bus (and the other products in the SOA Suite) is designed to help you solve the majority of common integration scenarios without writing any code. However, as one customer pointed out to me recently:

“You may not have to write any code, but you still need to write XPath which is just as complicated!”

Well, I’m not sure I agree XPath is as complicated as a programming language such as C or Java , but I can see where they are coming from. Learning XPath can seem hard without real world examples, so in the remainder of this post I am going to demonstrate by way of examples, some of the most common XPath expressions I see customers using.

Lets assume the XML below forms the message payload (i.e. the contents of the body variable, $body) in Oracle Service Bus and we are just using a Log action to print out the result.

<?xml version="1.0" encoding="UTF-8"?>
<p:PurchaseOrder id="1234" xmlns:p="
http://www.someshop.com/PurchaseOrder" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.someshop.com/PurchaseOrder PurchaseOrder.xsd ">
  <p:date>2010-04-02</p:date>
  <p:custID>C7123843</p:custID>
  <p:items>
    <p:item id="GF234324">
      <p:description>Denim Jeans</p:description>
      <p:department>Clothing</p:department>
      <p:price>30.99</p:price>
      <p:quantity>2</p:quantity>
    </p:item>
    <p:item id="HD312782">
      <p:description>iPod 80Gb White</p:description>
      <p:department>Electrical</p:department>
      <p:price>99.99</p:price>
      <p:quantity>1</p:quantity>
    </p:item>
    <p:item id="HD998775">
      <p:description>iPod Headphones</p:description>
      <p:department>Electrical</p:department>
      <p:price>19.99</p:price>
      <p:quantity>1</p:quantity>
    </p:item>
    <p:item id="KD123984">
      <p:description>Frying Pan</p:description>
      <p:department>Home</p:department>
      <p:price>9.99</p:price>
      <p:quantity>1</p:quantity>
    </p:item>
  </p:items>
  <p:cardDetails>
    <p:type>Mastercard</p:type>
    <p:cardNumber>1234-5678-1234-5678</p:cardNumber>
  </p:cardDetails>
</p:PurchaseOrder>

Below are the XPath expressions you will need to log the various information from the payload:

a) The purchase order id:

$body/pur:PurchaseOrder/@id

Note: The actual namespace prefix, i.e. pur (the default assigned by Oracle Service Bus in this case), does not have to match the namespace prefix in the original XML document (p), however they both need to point to the namespace URL http://www.someshop.com/PurchaseOrder.

b) The customer id:

$body/pur:PurchaseOrder/pur:custID/text()

Note: In contrast to attributes, XML elements (nodes) can contain a variety of different types of data or even other complex elements, hence the use of the text() function to get the textual data from the element.

c) The card number (assuming you don’t know its path):

$body//pur:cardNumber/text()

Note: The use of // in XPath is not advised (unless absolutely necessary) as it does involve searching through the whole XML tree and so is not  very performant particularly for large payloads.

d) The card number (assuming you don’t know its path or namespace):

$body//*:cardNumber/text()

e) The number of items in the purchase order:

count($body/pur:PurchaseOrder/pur:items/pur:item)

Note: We are using the count function and pointing to the repeating item element in the XML payload.

f) The id of the first item in the order:

$body/pur:PurchaseOrder/pur:items/pur:item[1]/@id

Note: We are using the [1] to select the first item – XPath is unlike most programming languages which index  arrays from 0 rather than 1.

g) The item(s) with id GF234324:

$body/pur:PurchaseOrder/pur:items/pur:item[@id="GF234324"]

Note: This expression will actually return a list of all items whose id attribute is GF234324 – in our example there is only one item with this id.

h) The description of the item with id GF234324:

$body/pur:PurchaseOrder/pur:items/pur:item[@id="GF234324"]/pur:description/text()

Note: This works because there is only one item with this id in the payload.

i) The electrical items in the purchase order:

$body/pur:PurchaseOrder/pur:items/pur:item[pur:department="Electrical"]

j) The second electrical item in the purchase order:

$body/pur:PurchaseOrder/pur:items/pur:item[pur:department="Electrical"][2]

k) The last item in the purchase order:

$body/pur:PurchaseOrder/pur:items/pur:item[last()]

Note: We are using the last() function here to determine the last item in the purchase order.

l) The second item in the purchase order:

$body/pur:PurchaseOrder/pur:items/pur:item[position()=2]

Note: We are using the position() function here to determine the second item in the purchase order.

m) The purchase order date in UK date format (i.e. DD/MM/YYYY)

translate('AB/CD/EFGH','EFGH-CD-AB', xs:string($body/pur:PurchaseOrder/pur:date))

Note: We are using the translate function here to convert the date to UK format. The first argument represents the output format (i.e. UK date format), the second argument represents the incoming format (i.e. XML schema date format) and the third argument represents the thing we are translating (i.e. the purchase order date).

Note: The translate function only works on strings and so we need to convert the purchase order date into a string, hence the use of the xs:string function.

 

If you’d like to get a simple introduction into XPath and related XML technologies I can highly recommend w3Schools who do free tutorials on a variety of XML technologies including one on XPath.

If you’d like to share any other XPath expressions you commonly use, please add them in the comments.

Comments:

This is very useful post mate.... I am gonna steal the stuff to my site... :)

Posted by nits on February 18, 2010 at 09:29 AM GMT #

Hello there. I want to congrat you by your article. It was really useful to me. I'm having some difficulty in interpreting this XML: <?xml version="1.0" encoding="UTF-8"?> <ReplaceLoyaltyTransactionListRespMsg xmlns="http://xmlns.oracle.com/ABCSImpl/Mimetica/ReplaceLoyaltyTransactionListMimeticaReqABCSImpl" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.oracle.com/ABCSImpl/Mimetica/ReplaceLoyaltyTransactionListMimeticaReqABCSImpl ReplaceLoyaltyTransactionListOut.xsd "> <ReplaceLoyaltyTransactionListResponse> <CancelledTransactionNumber>p:CancelledTransactionNumber</CancelledTransactionNumber> <FlightTransactionNumber>p:FlightTransactionNumber</FlightTransactionNumber> <PenaltyTransactionNumber>p:PenaltyTransactionNumber</PenaltyTransactionNumber> <NewFlightTransactionNumber>p:NewFlightTransactionNumber</NewFlightTransactionNumber> </ReplaceLoyaltyTransactionListResponse> <ErrorCode>p:ErrorCode</ErrorCode> <ErrorMessage>p:ErrorMessage</ErrorMessage> </ReplaceLoyaltyTransactionListRespMsg>" How do I select the specific node CancelledTransactionNumber? No mater how I tried, I can't get that value. I think that's because of the ReplaceLoyaltyTransactionListRespMsg atributes. If this node is replaced by a simple node like <ReplaceLoyaltyTransactionListRespMsg>, the value of CancelledTransactionNumber can be returned by /ReplaceLoyaltyTransactionListRespMsg/ReplaceLoyaltyTransactionListResponse/CancelledTransactionNumber/text() Otherwise, looks impossible. PS: Excuse my english. =)

Posted by Evandro Giachetto on February 20, 2010 at 04:07 AM GMT #

Many, many thanks!!! =) this example was not in any other page, and I read many of them. The magic was the /item[condition1][condition2] j) The second electrical item in the purchase order: $body/pur:PurchaseOrder/pur:items/pur:item[pur:department="Electrical"][2]

Posted by Nelson on February 23, 2010 at 11:03 AM GMT #

Nittin, Nelson - glad to hear my post was useful! Evandro - the challenge here is the use of the default namespace (i.e. the xmlns="..." attribute) in the ReplaceLoyaltyTransactionListRespMsg element. What this is effectively saying is that this element, and any elements contained within it are in the namespace http://xmlns.oracle.com/ABCSImpl/Mimetica/ReplaceLoyaltyTransactionListMimeticaReqABCSImpl. Hence, in order to refer to the CancelledTransactionNumber element you will need to specify that it (and all the elements above it) are within this namespace. The way to do this in Oracle Service Bus is to create a new namespace prefix: - Open the XQuery expression editor - this is the window opened whenever you want to add/set an XQuery expression in the built-in actions - Choose the Namespace Definition tab on the top right - Click the Add button - Set a prefix (your choice) and a URI (http://xmlns.oracle.com/ABCSImpl/Mimetica/ReplaceLoyaltyTransactionListMimeticaReqABCSImpl) Now you should be able to refer to the element using something like the following expression: $body/myPrefix:ReplaceLoyaltyTransactionListRespMsg/myPrefix:ReplaceLoyaltyTransactionListResponse/myPrefix :CancelledTransactionNumber/text() An alternative expression would be: $body//*:CancelledTransactionNumber/text() This is less performant than specifying the exact path, but often useful for testing. Hope that helps. Chris

Posted by Chris Tomkins on February 24, 2010 at 06:33 AM GMT #

Post a Comment:
  • HTML Syntax: NOT allowed
About

UK Pre-Sales consultant specialising in business integration.

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today