none
Get Xml node value using xpath c#

    Question

  • Hi!

    I am trying to get a node value in an xml file that looks like below. I want to get the boy with age =12 and get the value "sausage" of his food.I want to get "sausage" and put it in a string mystring using xpath in C#. I have tried a few times but all it returns is a "System.Xml.NodeList" Any ideas?

    <person>

    <boy age = "12">

    <color>yellow<\color>

    <food good="Y"> rice<\food>

    <food good="Y"> meat<\food>

    <food good="Y">sausage<\food>

    </boy>

    <\person>

    Tuesday, July 26, 2011 4:54 PM

Answers

  • For your sample xml try this:

    var xmlDoc2 = new XmlDocument();
    xmlDoc2.LoadXml(xml);
    
    var nodes = xmlDoc2.SelectNodes("/person/boy[@age=12]/food");
    nodes[2].InnerText = myString;
    

     

    But I think indexing is not a goood idea since the sequence may be random.


    "It's time to kick ass and chew bubble gum... and I'm all outta gum." - Duke Nukem
    • Marked as answer by snif123 Wednesday, July 27, 2011 7:59 AM
    Wednesday, July 27, 2011 5:45 AM

All replies

  • Well, there isn't enough information to differentiate the sausage from the other foods, your attribute good is always Y for all of the food nodes, so without knowing the InnerText of the node, xpath doesn't know which food element in particular you want.

    If you know you always have 3 food elements, and the 3rd is the one you always want, you can access the XmlNodeList with an indexer.

    XmlDocument d = new XmlDocument();
    d.Load(@"C:\foodlikes.xml");
    XmlNodeList n = d.GetElementsByTagName("food");
    if(n != null && n.Count == 3) {
    Console.WriteLine(n[2].InnerText); //Will output 'sasuage'
    }

    Or, you could always wrap it in a foreach loop and print each value.

    XmlDocument d = new XmlDocument();
    d.Load(@"C:\foodlikes.xml");
    XmlNodeList n = d.GetElementsByTagName("food");
    if(n != null) {
    foreach(XmlNode curr in n) {
    Console.WriteLine(curr.InnerText);
    }
    }

    Here is an example where you can specify that you want the boy of age 12, and food's good attribute is Y (I think this is what you asked for)

    XmlDocument d = new XmlDocument();
    d.Load(@"C:\foodlikes.xml");
    XPathNavigator nav = d.CreateNavigator();
    XPathExpression exp;
    exp = nav.Compile(@"/person/boy[@age='12']/food[@good='Y']");
    XPathNodeIterator iterator = nav.Select(exp);
     
    while(iterator.MoveNext()) {
    Console.WriteLine(iterator.Current.Value);
    }
     
    //Outputs:
    //rice
    //meat
    //sausage


    • Edited by Shadowfoxish Tuesday, July 26, 2011 5:41 PM added 3rd example
    • Proposed as answer by Do Django Tuesday, July 26, 2011 6:27 PM
    Tuesday, July 26, 2011 5:31 PM
  •       var s= @"<person>
    <boy age=""12"">
    <color>yellow</color>
    <food good=""Y""> rice</food>
    <food good=""Y""> meat</food>
    <food good=""Y"">sausage</food>
    </boy>
    </person>";
    
    
          var e = XElement.Parse(s);
          var boyElement = e.Descendants("boy").Where(e2 => e2.Attribute("age").Value.Equals("12")).First();
          var foods = boyElement.Descendants("food");
          var sausageNode = foods.Where(e3 => e3.Value.Equals("sausage")).First();
    

    That will get you the node you want, I am not exactly sure what you are trying to do from your description.
    • Proposed as answer by Do Django Tuesday, July 26, 2011 6:27 PM
    Tuesday, July 26, 2011 5:32 PM
  • First throws an exception if it does not exist, use FirstOrDefault to get null instead and check if it null.

    Descendants returns an empty IEnumerable if nothing matches.

    But sorry, this is Linq2Sql, do you really need xpath?

     

    var xmlDoc = XDocument.Parse(xml);
    var myString = "ketchup";
    xmlDoc.Descendants("food").First(x => Equals(x.Value, "sausage")).Value = myString;

     

    oops, ignore this, it is missing the age predicate, look at Patrick's reply


    No pressure, no diamonds.

    • Edited by Do Django Tuesday, July 26, 2011 6:32 PM
    Tuesday, July 26, 2011 5:45 PM
  • this is the xpath way:

     

    var xmlDoc2 = new XmlDocument();
    xmlDoc2.LoadXml(xml);
    var
     node = xmlDoc2.SelectSingleNode("/person/boy[@age=12]/food[text()='sausage']"); node.InnerText = myString;

     


    No pressure, no diamonds.

    (edited: missed the age predicate)
    Tuesday, July 26, 2011 5:53 PM
  • Yes, but since this was a simple forum post I didn't include all those checks.  Thank you for pointing out the deficiencies in my example.  I also didn't validate the input xml parsed.
    Tuesday, July 26, 2011 5:59 PM
  • It's something like this:

    doc.SelectSingleNode("//person/boy[@age=12]")


    Ctrl+Z
    Tuesday, July 26, 2011 6:25 PM
  • Hi Patrick, sorry, I just replied without reading the other replies, my fault, I just wanted to give some hints to snif

    Your linq reply is just perfect, just ignore mine please


    No pressure, no diamonds.
    Tuesday, July 26, 2011 6:26 PM
  • Thanks everyone!To clear something up, I would like to get all the foods of the boy age=12 in a nodelist/array from where I could randomly choose any of the array/list elements.

    @Barakoda- Your 1st solution ccant work for me.. I have many boys with age=13,14..etc so to using the tag"food", I would take all the foods in the doc yet I want only the age=12's food. Your 2nd solution looks exciting, let me try that.

    Tuesday, July 26, 2011 9:10 PM
  • Hi Do, thanks for your answer!

    Is there a way I could access the food elements using an index passed through the xpath? Here you in your example, you test for 'sausage' but I would like to use maybe food[2]?

    "/person/boy[@age=12]/food[text()='sausage']"
    Tuesday, July 26, 2011 9:13 PM
  • Thanks! I am trying to avoid XDocument but I may have to resort to it. Is there anyway to get the foods of the boy by indexing, to put all three foods in an array/nodelist and then I can choose by index which to get out?
    Tuesday, July 26, 2011 9:17 PM
  • For your sample xml try this:

    var xmlDoc2 = new XmlDocument();
    xmlDoc2.LoadXml(xml);
    
    var nodes = xmlDoc2.SelectNodes("/person/boy[@age=12]/food");
    nodes[2].InnerText = myString;
    

     

    But I think indexing is not a goood idea since the sequence may be random.


    "It's time to kick ass and chew bubble gum... and I'm all outta gum." - Duke Nukem
    • Marked as answer by snif123 Wednesday, July 27, 2011 7:59 AM
    Wednesday, July 27, 2011 5:45 AM
  • Or did you mean something like this?

     

    var nodes = xmlDoc2.SelectSingleNode("/person/boy[@age=12]/food[3]");

     


    "It's time to kick ass and chew bubble gum... and I'm all outta gum." - Duke Nukem
    Wednesday, July 27, 2011 6:30 AM
  • Thanks Do, your before last solution is perfect!

    Keep up the good work!

    Wednesday, July 27, 2011 8:00 AM