10

I have a hard time time figuring out how to properly search an xml document. I have been reading the other forms like crazy today but just can't seem to understand it. Was hopeing someone could give me a little more detailed information on how to do this the correct way and why using LINQ. Here is the XML file.

<?xml version="1.0" encoding="utf-8"?>
<body>
  <Customers>
    <Client>
      <Firstname Value="someguy" />
      <LastName Value="test" />
      <PhoneNumber Value="541555555" />
      <Address Value="55 nowhere" />
      <City Value="sometown" />
      <State Value="somestate" />
    </Client>
  </Customers>
</body>

What I am tyring to accomplish is to return all of the values of each element that matches by a name of a customer. Here is my code.

       IEnumerable<XElement> test = doc.Root.Descendants()
            .Where(nodeName => nodeName.Name == "Client"
            && nodeName.Descendants().Any(x => x.Name == "Firstname"
            && x.Value == "someguy"));

        foreach (XElement m in test)
        {
            MessageBox.Show(m.ToString());
        }

Would really appreciate the help. Please also if possible explain what the idea of using LINQ is like the format if you will. Not really sure how to explain what I am asking but for the most part just more understanding of the way it works or format etc...

EDIT

I have tried the solution given and still nothing is seeming to work. Please show me what I am doing wrong here.

        private void button2_Click(object sender, EventArgs e)
    {
        string seach = txtSearch.Text;

        XDocument doc = XDocument.Load(@"C:\users\tim\desktop\test.xml");

        var result = doc.Elements("Customers")
            .Elements("Client")
            .Where(x => x.Elements("Firstname")
                         .Where(c => c.Attribute("Value").Value == "someguy")
                         .Any())
            .ToList();

        foreach (var m in result)
        {
            MessageBox.Show(m.ToString());
        }
    }

EDIT:

Okay so I have gotten it to work now and output the data that I was looking for. Can someone please tell me if there is a more efficent way of doing what I have posted below this edit.

        private void button2_Click(object sender, EventArgs e)
    {
        string seach = txtSearch.Text;

        XDocument doc = XDocument.Load(@"C:\users\tim\desktop\test.xml");

        var result = (from clientNode in doc.Root.Descendants("Client")
                      from name in clientNode.Descendants("Firstname")
                      where name.Attribute("Value").Value == "someguy"
                      select new
                      {
                          Fname = clientNode.Element("Firstname").Attribute("Value").Value,
                          Lname = clientNode.Element("LastName").Attribute("Value").Value,
                          Phone = clientNode.Element("PhoneNumber").Attribute("Value").Value,
                          Address = clientNode.Element("Address").Attribute("Value").Value,
                          City = clientNode.Element("City").Attribute("Value").Value,
                          State = clientNode.Element("State").Attribute("Value").Value
                      });

        foreach (var m in result)
        {
            MessageBox.Show(m.Fname + "\n" +
                            m.Lname + "\n" +
                            m.Phone + "\n" +
                            m.Address + "\n" +
                            m.City + "\n" +
                            m.State);
        }
12
  • It's hard to understand what do you want. Do you want to get Client node if it has satisfying Firstname.Value="someguy" condition? Commented Sep 29, 2015 at 20:48
  • basically what I am trying to do is get all of the values of each element inside element that matches my condition for example. I am searching for "someguy" I need all the info his # his address etc... Commented Sep 29, 2015 at 20:56
  • 1
    @Timg XElement doc = XElement.Parse(xml); Commented Sep 30, 2015 at 0:56
  • so I have to put the xml into the code itself? vs accessing it from the individual file? Commented Sep 30, 2015 at 2:27
  • @Timg Did you read note part of my answer? Commented Sep 30, 2015 at 2:55

2 Answers 2

14

You can use XElement and search using a LINQ query like these:

XElement doc = XElement.Parse(xml);
var result = doc.Elements("Customers")
                .Elements("Client")
                .Where(x => x.Elements("Firstname")
                             .Where(c => c.Attribute("Value").Value == "someguy")
                             .Any())
                .ToList();

So with input:

var xml =
@"<?xml version=""1.0"" encoding=""utf-8""?>
<body>
    <Customers>
    <Client>
        <Firstname Value=""someguy"" />
        <LastName Value=""some last name"" />
        <PhoneNumber Value=""123456"" />
        <Address Value=""some where"" />
        <City Value=""some town"" />
        <State Value=""some state"" />
    </Client>
    <Client>
        <Firstname Value=""someotherguy"" />
        <LastName Value=""some other last name"" />
        <PhoneNumber Value=""123456"" />
        <Address Value=""some other where"" />
        <City Value=""some other town"" />
        <State Value=""some other state"" />
    </Client>
    </Customers>
</body>";

XElement doc = XElement.Parse(xml);
var result = doc.Elements("Customers")
                .Elements("Client")
                .Where(x => x.Elements("Firstname")
                             .Where(c => c.Attribute("Value").Value == "someguy")
                             .Any())
                .ToList();

The result will be:

<Client>
    <Firstname Value=""someguy"" />
    <LastName Value=""some last name"" />
    <PhoneNumber Value=""123456"" />
    <Address Value=""some where"" />
    <City Value=""some town"" />
    <State Value=""some state"" />
</Client>

And you can show values for example:

MessageBox.Show(string.Format("Firstname: {0}\nLastName: {1}\nPhoneNumber: {2}\nAddress: {3}\nCity: {4}\nState: {5}",
                result[0].Element("Firstname").Attribute("Value").Value,
                result[0].Element("LastName").Attribute("Value").Value,
                result[0].Element("PhoneNumber").Attribute("Value").Value,
                result[0].Element("Address").Attribute("Value").Value,
                result[0].Element("City").Attribute("Value").Value,
                result[0].Element("State").Attribute("Value").Value));

Note:

  • If you know the result should contain 0 or 1 elements, you can use FirstOrDefault() instead of ToList();
  • Element names are case sensitive so pay attention to Firstname for example.
  • You can use XElement.Load() to load from file for example XElement doc = XElement.Load(@"d:\file.xml");
  • The query will be more fault tolerant if you select elements this way .Where(c => c.Name.ToString().ToLower() == "Customers".ToLower())
  • The query will be more fault-tolerant if you select attributes this way .Where(c => c.Attributes("Value").Where(a=>a.Value == "someguy").Any())
Sign up to request clarification or add additional context in comments.

1 Comment

Good solution, but it should be noted that it does no checking if elements actually exist and will throw an exception if, for example, the Customers element is not present. Also, instead of .Elements("Customers").First(), you can use .Element("Customers").
4

As an alternative to @Reza Aghaei's solution, XPath is also a solution

var xml =
@"<?xml version=""1.0"" encoding=""utf-8""?>
<body>
    <Customers>
    <Client>
        <Firstname Value=""someguy"" />
        <LastName Value=""some last name"" />
        <PhoneNumber Value=""123456"" />
        <Address Value=""some where"" />
        <City Value=""some town"" />
        <State Value=""some state"" />
    </Client>
    <Client>
        <Firstname Value=""someotherguy"" />
        <LastName Value=""some other last name"" />
        <PhoneNumber Value=""123456"" />
        <Address Value=""some other where"" />
        <City Value=""some other town"" />
        <State Value=""some other state"" />
    </Client>
    <Client>
        <Firstname Value=""someguy"" />
        <LastName Value=""some other last name"" />
        <PhoneNumber Value=""12345634543"" />
        <Address Value=""some other where"" />
        <City Value=""some other town"" />
        <State Value=""some other state"" />
    </Client>
    </Customers>
</body>";
XElement doc = XElement.Parse(xml);

foreach(var client in doc
 .XPathSelectElements("./Customers/Client/Firstname[@Value='someguy']")
 .Select(x => x.Parent))
    Console.WriteLine (client);

If you prefer a Linq To Xml solution :

var results = (from c in doc.Descendants("Client")
            from f in c.Descendants("Firstname")
             where (string)f.Attribute("Value") == "someguy"
             select c).ToList();
foreach(var r in results)
    Console.WriteLine (r);

How to: Write Linq to Xml Queries with Complex Filtering

1 Comment

I will edit my main post but for some reason I have tried using the solutions you are giving me and still no dice.. I have no idea what I am doing wrong here please help me explain.

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.