Using xpath expressions in BizTalk

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

7 thoughts on “Using xpath expressions in BizTalk

  1. 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

  2. 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.

  3. If I understand correctly, what you would like would be to assign the following XML to an XMLDocument:

    <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>

    This can be done by using an expression shape with the following code:

    varXmlDoc = xpath(msgIncoming.MessagePart, "/*[local-name()='Root']/*[local-name()='Boxes']");

    This will assign the complete Boxes element including it’s subnodes and attributes to a variable of type XmlDocument.

  4. 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.

Leave a Reply

Your email address will not be published. Required fields are marked *