John R. Durant's WebLog

Blog of "The" Office Developer

XPath for a WordML and Outlook solution

The demo I did at Tech Ed 2005, Creating an Outlook Task Add-in Solution with VSTO 2005,  went off very well (I ended up receiving the Roku SoundBridge 2000 because of the good evals, so thanks to those who took the time to fill them out!!). Since then, I have been improving the solution, fixing bugs. The tester here, Pallavi, is tough on me in a very nice way! She wants the solution to be pretty sound, and so do I. To that end, I needed to make two key alterations:

1) Make the XPath query for searching my customers.xml case-insensitive

2) Make the XPath query in one of my XSLT's search sub-nodes in a specific way.

Today, I'll mention the first technique. One of the things I love about XML is that it is adaptive yet rigid. You can do amazing things with data and formatting, but the rules are kind of strict. One wrong move in your XPath, and you get bupkis. XML is case-sensitive. Searching XML can be challenging because the source XML may have elements with strings that have upper and lower case. But, the string you are looking for may or may not be cased when it is handed over to you.

In my solution, I solve this by doing two things. First, I convert the string I am looking for to lower case. Then, I use the translate() function to convert any upper-case in the string I will search to lower case. Using the translate() function to convert the element "name" contents to lower-case allows, in effect, a case-insensitive search:

mypath= "/contacts/contact[contains(translate(name, 'ABCDEFGHIJKLMNOPSQRTUVWXYZ', " _
&       " 'abcdefghijklmnopsqrtuvwxyz'),'" & customerName.ToLower & "]/."

Tomorrow, I'll show how I use the contains() function to search for a string in all sub-nodes of a given node.

 Rock Thought for the Day: I must say, the thought for the day is my Roku freakin' SoundBridge once again. I now can dial up music from my computer...while working in my garage! I am sure to do more projects there now. And, as my patient wife knows: every garage-project is an excuse to buy more tools.

Rock On

Published Thursday, August 11, 2005 3:03 PM by johnrdurant

Comments

 

Andrew Houghton said:

You Xpath has one side effect. The translate function is ideal for creating case-insensitive strings, but the contains function will find your customer name as a sub-string in the name node. For example if we were looking for "Andy" in the name node and it contained "Handy, Fred" your Xpath expression would match. This might be OK depending upon your situation, but it might not be.

I ran into this problem and used a little XSLT trickery. The trickery is slightly dependant on your situation, so modify as need be. Lets assume your name node contains names with the surname first as in my example above. What you want to do is find the word "Andy" not the sub-string "Andy". The first thing we will do is add a space at the beginning and ending of the name node. So the XPath now becomes:

mypath= "/contacts/contact[contains(translate(concat(' ', name, ' '), 'ABCDEFGHIJKLMNOPSQRTUVWXYZ', " _
& " 'abcdefghijklmnopsqrtuvwxyz'),'" & customerName.ToLower & "')]/."

Next we want to remove any word break characters, like punctuation, so you add the word break character to the find translate string but *not* the replace translate string. When you do that the Xpath translate function will remove the character because there is no coresponding character in the replace string. Now the Xpath becomes:

mypath= "/contacts/contact[contains(translate(concat(' ', name, ' '), 'ABCDEFGHIJKLMNOPSQRTUVWXYZ,', " _
& " 'abcdefghijklmnopsqrtuvwxyz'),'" & customerName.ToLower & "')]/."

Lastly we want to search for the customerName with spaces around it so we add them before and after it. The Xpath now becomes:

mypath= "/contacts/contact[contains(translate(concat(' ', name, ' '), 'ABCDEFGHIJKLMNOPSQRTUVWXYZ,', " _
& " 'abcdefghijklmnopsqrtuvwxyz'),' " & customerName.ToLower & " ')]/."

You now have a word search Xpath instead of a sub-string search Xpath.

BTW were missing the trailing single quote and right parenthesis around customerName.ToLower, e.g., "]/." should have been "')]/."; corrected in my examples.
August 12, 2005 5:37 PM
 

Oleg Tkachenko said:

And of course this is horribly I18n hostile. In a real world app I'd use something like str:lowercase() EXSLT.NET function.
August 14, 2005 5:52 AM
Anonymous comments are disabled

© 2009 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Microsoft
Page view tracker