tags:

views:

430

answers:

3

I have an XML document which looks something like this:

<meadinkent>
 <record>
  <comp_div>MENSWEAR</comp_div> 
  <sty_ret_type>ACCESSORIES</sty_ret_type> 
  <sty_pdt_type>BELTS</sty_pdt_type> 
  <pdt_category>AWESOME_BELTS</pdt_category> 
  </record>
<medinkent>

I want to useXPath to select nodes which match all four elements and I'm having trouble getting the boolean syntax right. I'm trying this to match the first two just as a test:

"/meadinkent/record/comp_div[.='" & comp_div & "'] and /meadinkent/record/sty_ret_type[.='" & sty_ret_type & "']"

Which is failing, saying no nodes are returned. Obviously I'm being very stupid - what am I doing wrong?

Cheers, mAtt

+6  A: 

Union
In order to get both nodes you need to use union operator - |

For example, the next query will return both type of nodes - comp_div and sty_ret_type:

/meadinkent/record/comp_div | /meadinkent/record/sty_ret_type

Filter by sub-nodes values
In order to filter node based on its sub-nodes values you need to place all conditions in same brackets [nodeA='value1' and nodeB='value2']

For example, the next query will return record nodes whose sub nodes match the filter:

/meadinkent/record[comp_div='MENSWEAR' and sty_ret_type='ACCESSORIES']

A C# union example:

[Test]
public void UnionExample()
{
    string xml =
        @"<meadinkent>
            <record>
                <comp_div>MENSWEAR</comp_div> 
                <sty_ret_type>ACCESSORIES</sty_ret_type> 
                <sty_pdt_type>BELTS</sty_pdt_type> 
                <pdt_category>AWESOME_BELTS</pdt_category>
            </record>
          </meadinkent>";

    XDocument xDocument = XDocument.Parse(xml);
    IEnumerable<XElement> selectedElements =
        xDocument.XPathSelectElements(
                "/meadinkent/record/comp_div | /meadinkent/record/sty_ret_type");

    Assert.That(selectedElements.Count(), Is.EqualTo(2));
}

A C# filter by sub-nodes example:

[Test]
public void FilterExample()
{
    string xml =
        @"<meadinkent>
            <record>
                <comp_div>MENSWEAR</comp_div> 
                <sty_ret_type>ACCESSORIES</sty_ret_type> 
                <sty_pdt_type>BELTS</sty_pdt_type> 
                <pdt_category>AWESOME_BELTS</pdt_category>
            </record>
          </meadinkent>";

    XDocument xDocument = XDocument.Parse(xml);
    IEnumerable<XElement> selectedElements =
        xDocument.XPathSelectElements(
       "/meadinkent/record[comp_div='MENSWEAR' and sty_ret_type='ACCESSORIES']");

    Assert.That(selectedElements.Count(), Is.EqualTo(1));
    Assert.That(selectedElements.First().Name.LocalName, Is.EqualTo("record"));
}
Elisha
Sorry, I haven't made myself clear. What I'm after is a reference to the Record node which matches all the element values inside. So if I want to assert that sty_pdt_type is "BELTS" and pdt_category is "AWESOMEBELTS" I want a reference to all the record nodes for which those elements match those values.
Matt Thrower
@Matt Thrower, I understand now what you meant :) Updated answer accordingly.
Elisha
Awesome, thanks!
Matt Thrower
+1  A: 

You just need to have the anded list of conditions inside the [ ] selector for record:

/meadinkent/record[compdiv[.='MENSWEAR'] and sty_ret_type[.='ACCESSORIES']]

Reads as: under a top level meadinkent node, a record node, having a child compdiv (with value MENSWEAR) and a child sty_ret_rype (with value ACCESSORIES).

AakashM
+1  A: 

or you could use linq to XML and do it like this :

var match = XElement.Parse(xmlString);

var matches = from xml in xmlDocument.Elements()
              where xml.Element("comp_div").Value == "MENSWEAR"
              && xml.Element("sty_ret_type").Value == "ACCESSORIES"
              && xml.Element("sty_pdt_type").Value == "BELTS"
              && xml.Element("pdt_category").Value == "AWESOME_BELTS"
              select xml;
Stephane