Xpath is a very nice way to retrieve values from BizTalk messages, especially when you can not use distinguished fields, for example in looping records. It can however be quite a complicated task as well, to find out how to retrieve a certain value. To that end, I have created a list of xpath filter expressions I commonly use. In these examples I will be using the following XML.
<root> <delivery> <deliverytype>Home</deliverytype> <boxinfo> <boxid>22</boxid> </boxinfo> </delivery> <delivery> <deliverytype>Work</deliverytype> <boxinfo> <boxid>35</boxid> </boxinfo> </delivery> <delivery> <deliverytype>Home</deliverytype> <boxinfo> <boxid>12</boxid> <boxid>87</boxid> </boxinfo> </delivery> <boxes> <box material="cardboard"> <id>12</id> <contents>Envelopes</contents> </box> <box material="cardboard"> <id>22</id> <contents>Surface Pro 2</contents> </box> <box material="plastic"> <id>35</id> <contents>Stickers</contents> </box> <box material="cardboard"> <id>87</id> <contents>Stamps</contents> </box> </boxes> </root> |
- Filter on index
Get the third delivery node./*[local-name()='Root']/*[local-name()='Delivery'][2]
Get the deliverytype node of the second delivery.
/*[local-name()='Root']/*[local-name()='Delivery'][2]/*[local-name()='DeliveryType']
- Filter on subnode text
Get all delivery nodes, which have a deliverytype of Home./*[local-name()='Root']/*[local-name()='Delivery'][*[local-name()='DeliveryType'][text()='Home']]
Get the deliverytype node of the delivery for BoxID 87.
/*[local-name()='Root']/*[local-name()='Delivery'][*[local-name()='BoxInfo']/*[local-name()='BoxID'][text()='87']]/*[local-name()='DeliveryType']
Get the deliverytype node, of the box which contains the stickers.
/*[local-name()='Root']/*[local-name()='Delivery'][*[local-name()='BoxInfo']/*[local-name()='BoxID'][text()=/*[local-name()='Root']/*[local-name()='Boxes']/*[local-name()='Box'][*[local-name()='Contents'][text()='Stickers']]/*[local-name()='ID']]]/*[local-name()='DeliveryType']
-
Combined filtering
You can also combine various filters in a single expression, making this very powerful.
Get the second delivery, where deliverytype is home./*[local-name()='Root']/*[local-name()='Delivery'][*[local-name()='DeliveryType'][text()='Home']][2]
Get the delivery, where deliverytype is work, and boxid is 35.
/*[local-name()='Root']/*[local-name()='Delivery'][*[local-name()='BoxInfo']/*[local-name()='BoxID'][text()='35']][*[local-name()='DeliveryType'][text()='Work']]
-
Logical operators
Get the deliveries, which contain boxid 12 OR boxid 22./*[local-name()='Root']/*[local-name()='Delivery'][*[local-name()='BoxInfo']/*[local-name()='BoxID'][text()='12' or text()='22']]
-
Get node text value
In the above examples, you are getting the actual node. Often though, you will want to get the (textual) value of a node. This can be done by placing the string expression around your xpath.
Get the value of the contents of the first box (Envelopes).string(/*[local-name()='Root']/*[local-name()='Boxes']/*[local-name()='Box'][1]/*[local-name()='Contents'])
-
Get count of node
Often you will want to count a certain node. To do this using xpath, place the count expression around your xpath.
Count how many deliveries there are (3).count(/*[local-name()='Root']/*[local-name()='Delivery'])
Count, how many deliveries with deliverytype home there are (2).
count(/*[local-name()='Root']/*[local-name()='Delivery'][*[local-name()='DeliveryType'][text()='Home']])
-
Get distinct values
Sometimes you will want to get the unique (distinct) values of a certain repeating node.
In this example, we get the distinct DeliveryTypes (in this case Work and Home), note that after preceding-sibling:: we first set the sibling node name, and then the name of the node for which we want to get the values./*[local-name()='Root']/*[local-name()='Delivery'][not(*[local-name()='DeliveryType']/text()=preceding-sibling::*[local-name()='Delivery']/*[local-name()='DeliveryType']/text())]/*[local-name()='DeliveryType']
The same principle can be used for attributes.
In this example, we get the distinct values for the box materials (cardboard and plastic), note that in this case we do not specify the sibling node name after preceding-sibling::, instead we immediatly access the attribute./*[local-name()='Root']/*[local-name()='Boxes']/*[local-name()='Box'][not(@material=preceding-sibling::*/@material)]/@material
This article is indeed helpful for people who are going to use Xpath in BizTalk.
I have a requirement where I need to extract the complete “Boxes” Record (Max occurs = 1) which in turn contains the Box Record which is repeating /unbounded.
This needs to be assigned to a new xml document which I would get into an untyped message part and output from the orchestration. I cannot use a new schema to define just the Boxes record and neither can I use helper classes. Would it be possible to do this just in expression and message construction shapes in BizTalk.
I would like the following to be extracted from the original message and get it assigned to a new xml document.
12
Envelopes
22
Surface Pro 2
35
Stickers
87
Stamps
Thanks
The XML tags somehow got removed when I copied the entire structure of “boxes” from the example xml you shared and only the values got copied. I would basically need the values as well as the xml tags.
If I understand correctly, what you would like would be to assign the following XML to an XMLDocument:
This can be done by using an expression shape with the following code:
This will assign the complete Boxes element including it’s subnodes and attributes to a variable of type XmlDocument.
Thanks Eldert. This has worked for me. I had gone through some posts which suggested to use XMLNodeList for this requirement but I was not able to accomplish using the same.
Thanks for your input.
Good article!
I’ve used DanSharps XmlViewer in the past to get the right XPath query based on a XML: http://dansharpxmlviewer.codeplex.com/
Thanks….really great work
Thank`s. Very helpfull for me as a beginner.