Link to home
Start Free TrialLog in
Avatar of Marc Davis
Marc DavisFlag for United States of America

asked on

Check for existence of XML node using LINQ

Hi,

I'm not very familiar with LINQ but this might be relatively simple.

I have an XML file (Test1.xml) like

<Test>
   <FirstName>Test</FirstName>
   <LastName>LastTest</LastName>
   <SSN>123456789</SSN>
   <State>OH</State>
   <LandLine>1112223456</LandLine>
   <VOIP>9876543211</VOIP>
</Test>
 
But I have another XML (Test2.xml) like:

<Test>
   <FirstName>Testa</FirstName>
   <LastName>LastTest2</LastName>
   <SSN>192837465</SSN>
   <State>CO</State>
   <LandLine>1112223457</LandLine>
</Test>

The VOIP does not exist in the second example.

I can go through the directory and get each of the files.

But I'm not too sure how to use LINQ to check for the existence of the node and how if it exists to check to see if it contains like a "654".

How can that be done? I'm using C#.

Any information would be greatly appreciated.

Thanks
Avatar of Member_2_6211924
Member_2_6211924

var query = 
from test in Xml.Descendants("Test")

let attributes = book.Element("VOIP")


select .......;

Open in new window


let attributes = book.Element("VOIP") ==> will return true if it exists
small edit: line 4 should be: let attributes = query.Element("VOIP")
Avatar of Marc Davis

ASKER

Get a compilation error. Cannot use local variable 'query' before it's declared.

And that is with the Line 4 edit.

Is there something else?
There is no special thing to do . Just check the element is null or not.

 
XDocument test1Xml = XDocument.Load(@"E:\EE\Test1.xml");
XDocument test2Xml = XDocument.Load(@"E:\EE\Test2.xml");

if (IsVOIPValue(test1Xml, "654"))
{
    MessageBox.Show("Test1");
}

if (IsVOIPValue(test2Xml, "654"))
{
    MessageBox.Show("Test2");
}

public bool IsVOIPValue(XDocument document, string value)
{
    if (document.Root.Element("VOIP") != null)
    {
        return string.Compare(document.Root.Element("VOIP").Value, value, StringComparison.InvariantCultureIgnoreCase) == 0;
    }

    return false;
}

Open in new window


But is there a way to do this with LINQ?

I'm looking at attempting to get a better understanding of LINQ to XML.
sorry, my bad (it's still early for me :-), the correct code should be:

var query = 
  from test in Xml.Descendants("Test")
  let attributes = test.Element("VOIP")
  select attributes;

Open in new window

The task you wanted to do is a very simple thing. The classes used here are the Linq To Xml Classes.

XDocument - Is the basic document xml class in XLinq.

The XLinq can be used in many contexts. You can create a Xml and search for a content in xml. And transforming Xml, .....

I just answered the question you asked.  :)

damn, forgot 1 letter...

line 3: let attributes = test.Elements("VOIP")
if you want to learn on XLinq then check tihs out...

http://www.hookedonlinq.com/
MicMatic, what would be the check for the query? Because it's executing but the query variable doesn't have anything in it. Basically, showing the resultsview as empty and that's on both of the .xml's.

parkash_prk, thanks for the book reference. You mentioned, "The task you wanted to do is a very simple thing." But what I don't understand is how that is done.
Take a look at this page, it has some nice info about LINQ & XML: http://www.techrepublic.com/blog/programming-and-development/access-xml-data-using-linq-to-xml/594

I think you'll find what you need, the info is quite good, and there are some nice examples!
oh ok. I will explain,

XDocument test1Xml = XDocument.Load(@"E:\EE\Test1.xml");

the above line will load the xml content from file to a XDocument object.

The XDocument Object represents your xml.
 
XDocument.Root -> points to the root node of the xml. In our Xml it points to "<Test>" node.

the FirstName, LastName,... all are children of the Test node. To access the children node you should use the Element() method of the XNode ( XElement, XDocument) are children of XNode.

check "XLINQ Classes" in the following link to see the class hierarchy diagram
http://www.codeproject.com/kb/vista/LINQ_3.aspx?PageFlow=Fluid

document.Root.Element("VOIP") -> that line gets the VOIP node. after that just check the value of the node.

Thanks & Regards
Prakash

You can cast the elements as string. Check for null to indicate an absent element.

e.g.

   
private void TestXmlLinq
        {
            const string xml1 = @"<Test>
                                    <FirstName>Test</FirstName>
                                    <LastName>LastTest</LastName>
                                    <SSN>123456789</SSN>
                                    <State>OH</State>
                                    <LandLine>1112223456</LandLine>
                                    <VOIP>9876543211</VOIP>
                                </Test>";
            const string xml2 = @"<Test>
                                    <FirstName>Testa</FirstName>
                                    <LastName>LastTest2</LastName>
                                    <SSN>192837465</SSN>
                                    <State>CO</State>
                                    <LandLine>1112223457</LandLine>
                                </Test>";

            using (TextReader reader = new StringReader(xml2))
            {
                XDocument testxml = XDocument.Load(reader);
                var people = from person in testxml.Descendants("Test") select new { FirstName = (string)person.Element("FirstName"), Voip = (string)person.Element("VOIP") };
                foreach (var person in people)
                    if (string.IsNullOrEmpty(person.Voip))
                    {
                        // element or value missing
                    }
                    else
                        if (person.Voip == "654")
                        {
                            // value == 654
                        }
            }

        }

Open in new window

Or does it depend on the structure of the XML as well? Albeit, I'm not sure how it would all the elements are based of Descendants("test"). If the VOIP is a childnode of it or somewhere further down in the XMLNode tree...is there a difference there?

In one, something like:

<Test>
   <FirstName>Test</FirstName>
   <LastName>LastTest</LastName>
   <SSN>123456789</SSN>
   <State>OH</State>
   <LandLine>1112223456</LandLine>
   <VOIP>9876543211</VOIP>
</Test>

In another something like:

<Test>
   <FirstName>Test</FirstName>
   <LastName>LastTest</LastName>
   <SSN>123456789</SSN>
   <State>OH</State>
   <Phone>
       <LandLine>1112223456</LandLine>
       <VOIP>9876543211</VOIP>
   <Phone>
</Test>



Avatar of Fernando Soto
Hi davism;

Here is a code snippet that will check for a VOIP node and see if it contains 654. If it does it returns true otherwise it returns false.

string[] test = { System.IO.File.ReadAllText(@"C:\Temp\test1.xml"), 
                      System.IO.File.ReadAllText(@"C:\Temp\test2.xml")};

foreach( string xmlText in test )
{	
	XDocument xdoc = XDocument.Parse(xmlText);
	XElement root = xdoc.Root;

	bool has654 = (from voip in root.Descendants()			 
    	           select voip).Any( v => v.Name == "VOIP" && v.Value.Contains("654"));
			  
	Console.WriteLine(has654);		
}

Open in new window


Fernando
FernandoSoto, that doesn't seem to work. I am always getting a "false".
Actually, that is working with that sample but for some reason it's not working on the XML that I am using.
Hi davism;

I tested the code with your sample data before posting with no issues. If the VOIP node exist but does NOT have the three digits 654 it will return False or if no VOIP node does not exist it will return False only if VOIP node has 654 in its inner text as this one. <VOIP>9876543211</VOIP>, it will return true.

Please post your code even if you copied and past.

Fernando
Then please post the code your are using and the sample that it is not working with.
Interesting... The very first node...or root node... <Test> if I just use that but if I use it with xmlnamespaces it does not.

It there a way to work with the namespaces?
Yes Linq to XML obeys the rules of XML so if a name space is used you need to use it as well in the query. Also be aware that XML is case sensitive so "VOIP" does not equal "voip".

foreach( string xmlText in test )
{	
    Document xdoc = XDocument.Parse(xmlText);
    Element root = xdoc.Root;
    // Add this line
    Namespace ns = root.GetDefaultNamespace();
    
    // Modify the select line with ns + before "VIOP"
    bool has654 = (from voip in root.Descendants()
                   select voip).Any( v => v.Name == ns + "VOIP" && v.Value.Contains("654"));
    		  
    Console.WriteLine(has654);		
}

Open in new window

That new line:

// Add this line
    Namespace ns = root.GetDefaultNamespace();

is giving me an error:

The type of namespace "Namespace" could not be found...

What would be the cause of that?  
Sorry that should read as follows:

XNamespace ns = root.GetDefaultNamespace();
That doesn't seem to be doing much either though.

The first node or root is like this:

<Test xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" TestID="Apple"  ID="1" xmlns="urn:Finite.List.Test">

I just inserted the root node you just posted into the XML documents and it worked just fine.

Can you post your source code please.
What I have is this:

string[] test = { (System.IO.File.ReadAllText(@"C:\Temp\test1.xml"))};
                    foreach( string xmlText in test )
                    {      
                          XDocument xdoc = XDocument.Parse(xmlText);
                          XElement root = xdoc.Root;
                        // Add this line
                        XNamespace ns = root.GetDefaultNamespace();

                        bool has654 = (from Phone in root.Descendants()
                                       select Phone).Any(v => v.Name == "PhoneNumber" && v.Value.Contains("("));
                    
                          Console.WriteLine(has654);
                    }

It's pretty much the same as what you provided. However, it's always back with a false.
ASKER CERTIFIED SOLUTION
Avatar of Fernando Soto
Fernando Soto
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Does it work for you now?
Oh my...where/how did I miss that ns + statement. I was wondering where the XNamespace ns comes in other than just defining it.

So far so good. Let me just continue on the testing to make sure before I award you the points.

Will provide info soon.

Thanks!
Not a problem, just wanting to make sure you were moving on.
Hi davism;

If this issue is resolved please close the question.

Thanks;
Fernando
Everything is working out great! Thank you very much and great information!
Not a problem, glad that It worked out for you.