0

I have the settings for my app in a simple XML document, like this:

<Settings>
    <Server>
        <Id>1</Id>
        <Name>SRV123456</Name> 
        <Par Type="Desktop" Region="Western">12</Par>
        <Par Type="Laptop" Region="Western">15</Par>
        <Par Type="Desktop" Region="Eastern">22</Par>
        <Par Type="Laptop" Region="Eastern">25</Par>
        <State>WA</State>
    </Server>
</Settings>

and am trying to query it using C# and LINQ, using this code:

xelement = XElement.Load(startpath + "\\Settings.xml");
var sn = from sl in xelement.Elements("Server")
       where (string)sl.Element("State") == "WA"
       where (string)sl.Element("Par").Attribute("Region") == "Western"
       where (string)sl.Element("Par").Attribute("Type") == "Desktop"
       select sl;

       foreach (XElement xele in sn)
       {
               Console.WriteLine(xele);
               Console.WriteLine(xele.Element("Par").Value);
       }

This works for the first "Par" value, and will return "12". But if I change

   where (string)sl.Element("Par").Attribute("Type") == "Desktop"

to

   where (string)sl.Element("Par").Attribute("Type") == "Laptop"

It doesn't return any results... what am I missing?

5
  • It's really ugly trying to mix both styles of querying. stick to either (preferably - the code version) Commented Sep 22, 2015 at 23:33
  • 1
    Element returns the first element in document order. The attributes on that element will be the one's that are checked. Commented Sep 22, 2015 at 23:40
  • There is no reason one would work and the other will not. Check you r spelling and upper/lowercase letters carefully. Commented Sep 22, 2015 at 23:40
  • 1
    @jdweng - there is a reason one would work and the other wouldn't. It has to do with the Element (vs Elements) and the order of the <Par> elements in the document. If <Par Type="Laptop" Region="Western">15</Par> was the first element named "Par" in the document, then the second line would work (and the first one wouldn't ) Commented Sep 22, 2015 at 23:44
  • @Tim - You should make that an answer. I'd upvote it. Commented Sep 22, 2015 at 23:45

3 Answers 3

1

Here's a query that works and looks prettier:

var parValue = xdoc.Descendants("Par")
    .Where(par=>par.Parent.Element("State")?.Value == "WA")
    .Where(par=>par.Attribute("Region")?.Value == "Western")
    .Where(par=>par.Attribute("Type")?.Value == "Laptop")
    .Select(par=>par.Value)
    .FirstOrDefault();

Note that the ?. is a CS6 feature, just omit the ? if using CS5, check for null if needed

Sign up to request clarification or add additional context in comments.

3 Comments

You can also do .Where(par => (string)par.Attribute("Region") == "Western") in earlier versions of C#. The explicit cast to string will safely return a null if the attribute is not found.
That worked perfectly - I just removed the ? and it returned the results every time. I wish I had posted this question this morning... Thanks!
@BigfootNick definitely do the explicit cast per Tim's comment to avoid NRE
0

I am by no means an expert on LINQ

However

    var sn = from sl in xelement.Elements("Server")
           where (string)sl.Element("State") == "WA"
           where (string)sl.Element("Par").Attribute("Region") == "Western"
           where (string)sl.Element("Par").Attribute("Type") == "Desktop"
           select sl;

I believe mean only return element where Attribute("Type") == "Desktop" means only return those where it equals "Desktop"...so when you loop there are none in your resultset

change to

    var sn = from sl in xelement.Elements("Server")
             where (string)sl.Element("State") == "WA"
             where (string)sl.Element("Par").Attribute("Region") == "Western"
              select sl;

and it should return all the types... not just "Desktop"

Comments

0

Maybe you're trying to return the Western/Laptop value = 15?

var state  = "WA";
var region = "Western";
var type   = "Laptop";
var xElement = XElement.Parse(@"<Settings>
    <Server>
        <Id>1</Id>
        <Name>SRV123456</Name> 
        <Par Type='Desktop' Region='Western'>12</Par>
        <Par Type='Laptop' Region='Western'>15</Par>
        <Par Type='Desktop' Region='Eastern'>22</Par>
        <Par Type='Laptop' Region='Eastern'>25</Par>
        <State>WA</State>
    </Server>
</Settings>");

foreach (XElement server in xElement.XPathSelectElements(
            String.Format("//Server[State='{0}']", state)))
{
    Console.WriteLine(server);

    // In your sample the Western/Desktop is the first element
    // If you want a specific Par element, you should query again with that filter
    foreach (XElement par in server.XPathSelectElements(
                String.Format("Par[@Region='{0}' and @Type='{1}']", region, type)))
        Console.WriteLine(par.Value); ;
}

I choose XPath over Linq to filter XML documents, as XPath seems more concise to me.

Comments

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.