2

I've got XML set up like this

<customers>
  <customer id="1">
    <name title="Mr" first="John" last="Smith" />
    <contact number="07123123123" email="[email protected]" />
    <address postcode="E1 1EW">1 Paper Street, London, England, GB</address>
  </customer>
  (...)
</customers>

I'm trying to query it with Linq to XML for learning purposes. So far I can XDocument.Load the file fine and add/remove etc. But I can't seem to figure out a way to query my XML documents for use in an if block. for example something like (Pseudo code):

XDocument document = XDocument.Load("People.xml");

if(
  exists(
    document.customers.customer.name.first.value("john") 
    && document.customers.customer.name.last.value("smith")
  )
)
{ 
   bool exists = true;
}

Whatever I try something the compiler will either laugh at me or spit out something about how it cannot implicitly convert an Ienumerable bool to a bool.

I've been trying many combinations of things from many different google searches for a while now and I think guessing is starting to do more harm than good, can anybody provide a C# snippet that would work in an if block for my xml setup? Just to see if first and last name exist together in the same node. Usually once I see something actually work I can take it from there. Most of the questions I find on the net are only searching to see if the entire node exists or are only searching for one attribute and I just can't seem to tailor it.

(Before anybody bursts into flames over my XML structure, this is not for production purposes, I just want to get a grasp of using this in case I need to in an upcoming project.)

Bonus love for anybody that can link any documentation that's not MSDN (I already have about 10 tabs open on it) and involves some good low level/beginner tutorials on Linq to XML.

3
  • What have you tried? That way we can not only post the right answer, but also tell you where you went wrong. Commented May 31, 2013 at 14:31
  • You need to start by querying the Document.Root
    – IAbstract
    Commented May 31, 2013 at 14:34
  • I fully understand that that'd be helpful to you guys but where I've been jury rigging it for a few hours now, then deleting it out of frustration to try the next thing, I now wouldn't be able to detail what I've already done as I haven't kept track of it explicitly. It probably would have been horrifically malformed code anyway.
    – Dicckk
    Commented May 31, 2013 at 14:41

4 Answers 4

3

To check for John Smith exists as a customer in your XML you ll use

XDocument doc = XDocument.Load(Path);
var customers = doc.Descendants("customer");    
//To Check for John Smith    
if (customers.Elements("name")
             .Any(E => E.Attribute("first").Value == "John" 
                    && E.Attribute("last").Value == "Smith"))
{
    //Do your thing
}
7
  • Works beautifully. Thank you so much!
    – Dicckk
    Commented May 31, 2013 at 14:50
  • Have you got any idea how postcode could be included as well? It's a level up from the first/last attrib but doing customers.any(E => E.Element("name").Attribute("first").Value == "John" && E.Element("address").Attribute("postcode").Value == "E1 1EW") doesn't seem to work.
    – Dicckk
    Commented May 31, 2013 at 15:46
  • You cant do that. Because your selector E is looking at name element like <name title="Mr" first="John" last="Smith" />.You have a customer.Elements("name") selector. So to check for postal code you ll have to go a level up and do something like if (customers.Any(E => E.Element("name").Attribute("first").Value == "John" && E.Element("name").Attribute("last").Value == "Smith" && E.Element("address").Attribute("postcode").Value == "E1 1EW" ))
    – arunlalam
    Commented May 31, 2013 at 16:05
  • I've tried that and it doesn't seem to work. Should it work? If so then the problem might be elsewhere in my code
    – Dicckk
    Commented May 31, 2013 at 16:15
  • It just seems to be returning false (that John Smith @ E1 1EW doesnt exist) at all times. If I spam the "add" button I have set up it just constantly adds new John Smiths at E1 1EW without the messageBox.Show firing to warn that those details already exist.
    – Dicckk
    Commented May 31, 2013 at 16:27
0

Created a boolean called check to hold the result. do a count on how many elements called name have attributes First=john and last=smith Then set the result to check variable.

BTW your XML was missing a " in front of John. That will cause you problems :)

bool check;
var res = XDocument.Load(@"c:\temp\test.xml");
var results = res.Descendants("name")
                 .Where(x => x.Attribute("first").Value == "john" && x.Attribute("last").Value == "smith")
                 .Select(x => x.Elements()).Count();

check = results != 0;
0

XPath is your friend

using System.Xml.XPath;
...

void foo(){
    XDocument document = XDocument.Load("People.xml");

    var firstCustomerNode = document.XPathSelectElement(
        "/customers/customer[0]/name"
        );
    var hasfirstNameAndLastName = firstCustomerNode.Attribute("firstname") != null && firstCustomerNode.Attribute("lastname") != null;

    if(hasfirstNameAndLastName)
    {

    }
}

Please note that you may also use a schema to validate your Xml, but it's more complex to write.

If you don't want to use XPath, you may also write:

var firstCustomerNode = document.Root.Element("Customers").Elements("customer").First().Element("name");

But honestly, the XPath query is far more readable, and will simplify the error check. This code suppose all the elements up to the target node exists. If not, your code will fail with a poor NullReferenceException.

4
  • This method is defined in the System.Xml.Linq assembly. So it is link to xml... but does it matter? Would you use a screwdriver to paint?
    – Steve B
    Commented May 31, 2013 at 14:37
  • @newStackExchangeInstance: this should be perfectly working, why do think this does not works? If I introduce a typo, let me know, I'll correct it.
    – Steve B
    Commented May 31, 2013 at 14:43
  • In your XPath example, there is no nullcheck for lastname. Also, I find LINQ to XML far more readable, but opinions are opinions. Commented May 31, 2013 at 14:47
  • I think LINQ is much nicer to read. It also has performance gains over XPath(see Performance Differences section here: msdn.microsoft.com/en-us/library/bb675156.aspx)
    – Lotok
    Commented May 31, 2013 at 14:50
0

You need to start by querying the Document.Root.Descendants. Then a method such as IfExists

var nodes = document.Root.Descendants("customer");

bool IfExists(string FirstName, string LastName) {
    return nodes.Elements("name").Any(node => 
        node.Attribute("first").Value.Equals(FirstName) &&
        node.Attribute("last").Value.Equals(LastName));
}

You may want to add exception handling in case an attribute is missing or contains an empty value.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.