4

I am trying to figure out how to find all matches of string in a XmlDocument.

XmlNodeList results 
      = document.SelectNodes("Products/Product/fn:matches(.,'" + SearchWord + "')");

Im trying to compare the innerText of Product.

The above example don't work though, but I guess my way of using XPath functions are very wrong.

Andreas Rohde
  • 613
  • 5
  • 15
user750508
  • 161
  • 2
  • 2
  • 10
  • What string are you trying to find a match on ...? give an example please – MethodMan Jan 31 '12 at 22:27
  • possible duplicate: http://stackoverflow.com/questions/157044/how-do-i-resolve-the-error-expression-must-evaluate-to-a-node-set-when-checkin – M.Babcock Jan 31 '12 at 23:09

4 Answers4

5

Evaluate this XPath 1.0 expression (did you know matches() is an XPath 2.0 function and isn't supported in .NET):

Products/Product/descendant::*[contains(text(), 'YourSearchWord')]

This selects all elements that have a text-node-child that contains the string 'YourSearchWord' and that are descendents of a Product element that is a child of a Products element that is a child of the current (context) node.

You can compose the XPath expression with:

string.Format("Products/Product/descendant::*[contains(text(), '{0}')]", 
              SearchWord )

However, if SearchWord is obtained from user input, it is recommended never to include it in a skeletal string as above, so that XPath injection will be avoided.

If this is the case, the recommended method is to have a precompiled XPath expression in which the user input will be referenced as a variable and the value of this variable will be consumed from the XPath evaluation context.

More details how to prevent an XPath injection can be found in this answer:

https://stackoverflow.com/a/6393690/36305

Community
  • 1
  • 1
Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431
2

Lets say you have the following xml

<Names>
    <Name>
        <FirstName>John</FirstName>
        <LastName>Smith</LastName>
    </Name>
    <Name>
        <FirstName>James</FirstName>
        <LastName>White</LastName>
    </Name>
</Names>

To get all nodes use XPath expression /Names/Name. The first slash means that the node must be a root node. SelectNodes method returns collection XmlNodeList which will contain the nodes. To get value of sub node you can simply index XmlNode with the node name: xmlNode["FirstName"].InnerText.

XmlDocument xml = new XmlDocument();
xml.LoadXml(myXmlString); // suppose that myXmlString contains "<Names>...</Names>"

XmlNodeList xnList = xml.SelectNodes("/Names/Name");
foreach (XmlNode xn in xnList)
{
  string firstName = xn["FirstName"].InnerText;
  string lastName = xn["LastName"].InnerText;
  Console.WriteLine("Name: {0} {1}", firstName, lastName);
}

The output is:

Name: John Smith Name: James White

use this as an example / starting point. thanks

MethodMan
  • 18,625
  • 6
  • 34
  • 52
  • Is there no way of doing it directly in the SelectNodes call? – user750508 Jan 31 '12 at 22:38
  • What I am trying to do is basically get all products that matches the search query, but I do not want to look through all products. I want XPath to take care of it for me – user750508 Jan 31 '12 at 22:40
  • +1. While using XPath to do everything is fun to achieve, you may be better of doing simple parts with XPath and other filtering with code as DJ suggested. I.e. original question contains "XPath injection" bug (similar to "SQL injection") i.e. if SearchWord contains double quote your XPath will fall apart. – Alexei Levenkov Feb 01 '12 at 01:20
  • @AlexeiLevenkov: The way to avoid XPath injection and still construct and evaluate a single XPath expression is to have a precompiled XPath expression in which the user input will be referenced as a variable and the value of this variable will be consumed from the XPath evaluation context. – Dimitre Novatchev Feb 01 '12 at 13:17
0

If I understand your question correctly, you can use the following:

"Products/Product/[.='" + SearchWord + "']"

or use functions such as contains(), starts-with(), or normalize-space() (trims and replaces consecutive white space with one space)

"Products/Product/[contains(normalize-space(.), '" + SearchWord + "')]"

Cameron S
  • 2,251
  • 16
  • 18
0

From the answer to this duplicate question:

The expression given evaluates to a boolean, not a node-set. I assume you want to check whether the ProjectName equals the parametrized text. In this case you need to write

//ErrorTable/ProjectName[text()='{0}']

This gives you a list of all nodes (a nodeset) matching the given condition. This list may be empty, in which case the C#-Expression in your sample will return null.

As an afterthought: You can use the original xpath expression, but not with SelectSingleNode, but with Evaluate, like this:

(bool)xmlDocument.CreateNavigator().Evaluate(String.Format("//ErrorTable/ProjectName/text()='{0}'", projectName));
Community
  • 1
  • 1
M.Babcock
  • 18,753
  • 6
  • 54
  • 84