LINQ to XML Question

Getting Subnode values

  • (2 Pages)
  • +
  • 1
  • 2

15 Replies - 7086 Views - Last Post: 13 November 2009 - 08:51 AM Rate Topic: -----

#1 NickDMax  Icon User is offline

  • Can grep dead trees!
  • member icon

Reputation: 2250
  • View blog
  • Posts: 9,245
  • Joined: 18-February 07

LINQ to XML Question

Posted 09 November 2009 - 02:54 PM

Ok so I have a returned data set in a format like this:
<results>
	<result>
		<item>
			<name>COLL_NAME</name>
			<value>ID</value>
		</item>
		<item>
			<name>DESCRIPTION</name>
			<value>Collumn ID</value>
		</item>
	</result>
	<result>
		<item>
			<name>COLL_NAME</name>
			<value>UNAME</value>
		</item>
		<item>
			<name>DESCRIPTION</name>
			<value>User Name</value>
		</item>
	</result>
	<result>
		<item>
			<name>COLL_NAME</name>
			<value>FNAME</value>
		</item>
		<item>
			<name>DESCRIPTION</name>
			<value>First Name</value>
		</item>
	</result>
	...
</results>


and I would like to convert this into a Manageable object such as a dictonary where each result would have the key equal to item[0].value, and the value equal to item[1],value

But LINQ totally confuses me and I have no idea how to get from point A to point B or even some midpoint that I can work from.

truth to be known I am not even sure how to get the second item from the result using X-DOM. I suppose I have to do nested loop foreach-reuslt and then foreach-item... There has got to be an easier way... Any hints?

SO far I can't even get this to work:
Dictionary<string, string> mannagable = new Dictionary<string, string>(10);
XDocument resultsDoc = Xdocument.Parse(result);
foreach (XElement r in resultsDoc.Descendants("result")) {
	foreach(XElement item in r.Elements("item") ){
		Console.WriteLine(item);
	}
}


All of the LINQ to XML examples I find have these ultra-simple xml files... I just need to get my head wrapped arround how this might work with a real life XML file.

Is This A Good Question/Topic? 0
  • +

Replies To: LINQ to XML Question

#2 NickDMax  Icon User is offline

  • Can grep dead trees!
  • member icon

Reputation: 2250
  • View blog
  • Posts: 9,245
  • Joined: 18-February 07

Re: LINQ to XML Question

Posted 10 November 2009 - 10:07 AM

SO the basic way I ended up parsing this into a Dictionary was:
	class Testing {

	   static void Main(string[] args) {
		   string inXMLStr =
"<results>" +
"	<result>" +
"		<item>" +
"			<name>DESCRIPTION</name>" +
"			<value>Column ID</value>" +
"		</item>" +
"		<item>" +
"			<name>COLL_NAME</name>" +
"			<value>ID</value>" +
"		</item>" +
"	</result>" +
"	<result>" +
"		<item>" +
"			<name>COLL_NAME</name>" +
"			<value>UNAME</value>" +
"		</item>" +
"		<item>" +
"			<name>DESCRIPTION</name>" +
"			<value>User Name</value>" +
"		</item>" +
"	</result>" +
"	<result>" +
"		<item>" +
"			<name>COLL_NAME</name>" +
"			<value>FNAME</value>" +
"		</item>" +
"		<item>" +
"			<name>DESCRIPTION</name>" +
"			<value>First Name</value>" +
"		</item>" +
"	</result>" +
"</results>";
			Dictionary<string, string> mannagable = new Dictionary<string, string>(10);
			XDocument resultsDoc = Xdocument.Parse(inXMLStr);
			foreach (XElement rslt in resultsDoc.Descendants("result"))
			{
				string temp = "";
				foreach (XElement item in rslt.Elements("item"))
				{
					//Console.WriteLine(item);
					if ("COLL_NAME".Equals(item.Element("name").Value))
					{
						if ("".Equals(temp))
						{
							temp = item.Element("value").Value;
						}
						else
						{
							mannagable.Add(item.Element("value").Value, temp);
							temp = "";
						}
					}
					else if ("DESCRIPTION".Equals(item.Element("name").Value))
					{
						if ("".Equals(temp))
						{
							temp = item.Element("value").Value;
						}
						else
						{
							mannagable.Add(temp, item.Element("value").Value);
							temp = "";
						}
					}
				}
			}

			foreach (string k in mannagable.Keys)
			{
				Console.WriteLine("{0} = \"{1}\"", k, mannagable[k]);
			}
			Console.WriteLine("done");
			Console.ReadKey();
		}
	}


but I still think that this can be done with LINQ much easier -- I think it is all in just understanding LINQ...
Was This Post Helpful? 0
  • +
  • -

#3 baavgai  Icon User is online

  • Dreaming Coder
  • member icon

Reputation: 5777
  • View blog
  • Posts: 12,591
  • Joined: 16-October 07

Re: LINQ to XML Question

Posted 10 November 2009 - 12:04 PM

That's the second time today I've been unimpressed with Linq. :P

At little XPath goes a long way, no linq required.

Dictionary<string, string> mannagable = new Dictionary<string, string>(10);
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(inXMLStr);
foreach (XmlElement colNameNode in xmlDoc.SelectNodes("//item[name='COLL_NAME']/value")) {
	XmlElement descNode = (XmlElement)colNameNode.SelectSingleNode("../../item[name='DESCRIPTION']/value");
	mannagable.Add(colNameNode.InnerText, descNode.InnerText);
}

foreach (string k in mannagable.Keys) {
	Debug.WriteLine(string.Format("{0} = \"{1}\"", k, mannagable[k]));
}


Was This Post Helpful? 1
  • +
  • -

#4 NickDMax  Icon User is offline

  • Can grep dead trees!
  • member icon

Reputation: 2250
  • View blog
  • Posts: 9,245
  • Joined: 18-February 07

Re: LINQ to XML Question

Posted 10 November 2009 - 03:25 PM

Indeed I may go with XPath in the final version (esp. seeing your example).

For now I am looking into LINQ because the customer is enamored with it, and I am *supposed* to be some kind of expert (I hate that as a consultant you often get advertised as having more knowledge than you do). Though to be honest I have already told them that I have not really used C# in two years and LINQ really post dates my experience.

I came up with this:
var resultsVar = from di in resultsDoc.Descendants("results") 
	select new  { 
		dataItems = from it in di.Elements("item") select new { 
			name = it.Element("name").Value,
			value = it.Element("value").Value 
		} 
	};


This is *slightly* more manageable than the XML but still kind of useless.

but it is a step closer.

See the above example is just a basic example, I will need to deal with result XMLs that may have a number of different name-value pairs describing each object. Ideally I would like to be able to map directly from the name-value pairs to an object but I don't know if I have time to think my way though that maze... I do think that LINQ can do it... I just don't know if it is within my grasp.
Was This Post Helpful? 0
  • +
  • -

#5 NickDMax  Icon User is offline

  • Can grep dead trees!
  • member icon

Reputation: 2250
  • View blog
  • Posts: 9,245
  • Joined: 18-February 07

Re: LINQ to XML Question

Posted 10 November 2009 - 03:41 PM

getting a little bit closer because I learned I can do this:


var resultsVar = from di in resultsDoc.Descendants("result") 
								 select new Dictionary<string, string>{ { "a","b" }};


so now I just need to replace the strings "a" and "b" with the values and tada... so close I can taste it!
Was This Post Helpful? 0
  • +
  • -

#6 lesPaul456  Icon User is offline

  • D.I.C Addict
  • member icon

Reputation: 173
  • View blog
  • Posts: 729
  • Joined: 16-April 09

Re: LINQ to XML Question

Posted 10 November 2009 - 06:09 PM

Here's one way to do this. It's not the best way, but it works (at least it should :P ).

// Load the file.
XDocument doc = Xdocument.Load("XmlFile");

// Get all the elements.
var results = from result in doc.Descendants("result").Elements()
					select new
					{
						  Key = result.Element("name").Value,
						  Value = result.Element("value").Value
					};

// Loop through each element and add it to the dictionary.
foreach (var result in results)
{
	 mannagable.Add(result.Key, result.Value);
}



Hope this helps!
Was This Post Helpful? 1
  • +
  • -

#7 NickDMax  Icon User is offline

  • Can grep dead trees!
  • member icon

Reputation: 2250
  • View blog
  • Posts: 9,245
  • Joined: 18-February 07

Re: LINQ to XML Question

Posted 11 November 2009 - 09:49 AM

SO this was another LINQ attempt that is making a little progress based upon lesPaul456's example...
namespace Sandbox{
	class Testing {

	   static void Main(string[] args) {
	   //static void tMain(string[] args) {
		   string inXMLStr =
"<results>" +
"	<result>" +
"		<item>" +
"			<name>COLL_NAME</name>" +
"			<value>ID</value>" +
"		</item>" +
"		<item>" +
"			<name>DESCRIPTION</name>" +
"			<value>Column ID</value>" +
"		</item>" +
"	</result>" +
"	<result>" +
"		<item>" +
"			<name>COLL_NAME</name>" +
"			<value>UNAME</value>" +
"		</item>" +
"		<item>" +
"			<name>DESCRIPTION</name>" +
"			<value>User Name</value>" +
"		</item>" +
"	</result>" +
"	<result>" +
"		<item>" +
"			<name>COLL_NAME</name>" +
"			<value>FNAME</value>" +
"		</item>" +
"		<item>" +
"			<name>DESCRIPTION</name>" +
"			<value>First Name</value>" +
"		</item>" +
"	</result>" +
"</results>";
			
			XDocument resultsDoc = Xdocument.Parse(inXMLStr);
			var resultsVar = from di in resultsDoc.Descendants("result")
							 select new { Key = di.Elements().ElementAt(0).Element("value").Value, Value = di.Elements().ElementAt(1).Element("value").Value };

			foreach (var item in resultsVar)
			{
				Console.WriteLine("{0} = \"{1}\"", item.Key, item.Value);
			}
			Console.WriteLine("done");
			Console.ReadKey();
		}
	}
}


The reason I don't really like this one is that it depends upon the format of the XML -- i.e. it uses ElementAt() rather than some sort of selector that might ensure that I have the right elements.

I think LINQ seem wonderful if the XML you are dealing with is simple, but something common like a list of name-value pairs to describe and object is actually very common in my experience and LINQ is just being miserable. This is a common serialization technique and is not turning out to be very convenient.
Was This Post Helpful? 0
  • +
  • -

#8 NickDMax  Icon User is offline

  • Can grep dead trees!
  • member icon

Reputation: 2250
  • View blog
  • Posts: 9,245
  • Joined: 18-February 07

Re: LINQ to XML Question

Posted 11 November 2009 - 10:04 AM

I just discovered the XPathEvaluate member of XContainer... perhaps this will allow me to make the selection a little more definite.
Was This Post Helpful? 0
  • +
  • -

#9 lesPaul456  Icon User is offline

  • D.I.C Addict
  • member icon

Reputation: 173
  • View blog
  • Posts: 729
  • Joined: 16-April 09

Re: LINQ to XML Question

Posted 11 November 2009 - 10:36 AM

Quote

The reason I don't really like this one is that it depends upon the format of the XML -- i.e. it uses ElementAt() rather than some sort of selector that might ensure that I have the right elements.


The code I posted allows you to do what you're asking without using ElementAt().

This part here grabs all the child nodes of each "result" node...

var results = from result in doc.Descendants("result").Elements()
					select new
					{
						  Key = result.Element("name").Value,
						  Value = result.Element("value").Value
					};



...and then gets the key and value pair that each child element conatins.

As long as there is a "result" element with a child that has a "name" and "value" element, the code should work. This allows for some variation in the format of the xml file.

Quote

I think LINQ seem wonderful if the XML you are dealing with is simple, but something common like a list of name-value pairs to describe and object is actually very common in my experience and LINQ is just being miserable. This is a common serialization technique and is not turning out to be very convenient.


LINQ-to-XML can in fact handle large, complex xml files. It just takes a little more work.
Was This Post Helpful? 0
  • +
  • -

#10 NickDMax  Icon User is offline

  • Can grep dead trees!
  • member icon

Reputation: 2250
  • View blog
  • Posts: 9,245
  • Joined: 18-February 07

Re: LINQ to XML Question

Posted 11 November 2009 - 11:27 AM

sorry lesPaul456 -- your code just does not do what I need. (your code and my code produce very different results). There was a reason I used the ElementAt -- I don't just need to grab the "name" and "value" elements, I need to associate the "value" fields into an object. I really could care less about the name field -- its only use to me is to help make sure I map the value to the correct member field in an object.

I also need techniques that will allow me to deal with more complex versions of this name-value pair serialization because not all of the object I will be dealing with are quite as simple.

Quote

LINQ-to-XML can in fact handle large, complex xml files. It just takes a little more work.
At this point I disagree. when it comes to deserialization/unmarshaling LINQ to XML is neat, but I think that unless one is working with querying something simple there are other technologies which are far better suited to the task.

On the other hand it would seems as though serialization/marshaling of objects with LINQ-to-XML would be fantastic.

On the other hand -- my point of view may just be that I have not seen enough of LINQ. -- I really don't feel that it has "clicked" for me yet.
Was This Post Helpful? 0
  • +
  • -

#11 NickDMax  Icon User is offline

  • Can grep dead trees!
  • member icon

Reputation: 2250
  • View blog
  • Posts: 9,245
  • Joined: 18-February 07

Re: LINQ to XML Question

Posted 11 November 2009 - 11:55 AM

See this is what I mean by it has not "clicked" yet...

			var resultsVar = from di in resultsDoc.Descendants("result")
							 //select new { Key = di.Elements().ElementAt(0).Element("value").Value, Value = di.Elements().ElementAt(1).Element("value").Value };
							 select new { 
								 Key = (from item in di.Elements("item") where "COLL_NAME".Equals(item.Element("name")) select item.Element("value")),
								 Value = (from item in di.Elements("item") where "DESCRIPTION".Equals(item.Element("name")) select item.Element("value"))
							 };


this resulted in this output:
System.Linq.Enumerable+WhereSelectEnumerableIterator`2[System.Xml.Linq.XElement, System.Xml.Linq.XElement] = "System.Linq.Enumerable+WhereSelectEnumerableIterator`2[System.Xml.Linq.XElement,System.Xml.Linq.XElement]"
System.Linq.Enumerable+WhereSelectEnumerableIterator`2[System.Xml.Linq.XElement, System.Xml.Linq.XElement] = "System.Linq.Enumerable+WhereSelectEnumerableIterator`2[System.Xml.Linq.XElement,System.Xml.Linq.XElement]"
System.Linq.Enumerable+WhereSelectEnumerableIterator`2[System.Xml.Linq.XElement, System.Xml.Linq.XElement] = "System.Linq.Enumerable+WhereSelectEnumerableIterator`2[System.Xml.Linq.XElement,System.Xml.Linq.XElement]"


It would seem that I just don't understand the results of LINQ expressions.
Was This Post Helpful? 0
  • +
  • -

#12 lesPaul456  Icon User is offline

  • D.I.C Addict
  • member icon

Reputation: 173
  • View blog
  • Posts: 729
  • Joined: 16-April 09

Re: LINQ to XML Question

Posted 11 November 2009 - 12:05 PM

First off, sorry about the confusion. I thought you were just trying to grab the name and value from each child element.

If you told me exaclty what the output needs to be I may be able to give you a better example.

Until then I suggest you look up some samples about of using LINQ-to-XML. It actually is quite powerful and adaptable (I've used it to read WordML files).
Was This Post Helpful? 0
  • +
  • -

#13 NickDMax  Icon User is offline

  • Can grep dead trees!
  • member icon

Reputation: 2250
  • View blog
  • Posts: 9,245
  • Joined: 18-February 07

Re: LINQ to XML Question

Posted 11 November 2009 - 12:07 PM

And the final copy
using System;
using System.Collections.Generic;
using System.Xml.Linq;
using System.Linq;
using System.Text;

namespace VRDCall {
	class Testing {

	   static void Main(string[] args) {
	   //static void tMain(string[] args) {
		   string inXMLStr =
"<results>" +
"	<result>" +
"		<item>" +
"			<name>COLL_NAME</name>" +
"			<value>ID</value>" +
"		</item>" +
"		<item>" +
"			<name>DESCRIPTION</name>" +
"			<value>Column ID</value>" +
"		</item>" +
"	</result>" +
"	<result>" +
"		<item>" +
"			<name>COLL_NAME</name>" +
"			<value>UNAME</value>" +
"		</item>" +
"		<item>" +
"			<name>DESCRIPTION</name>" +
"			<value>User Name</value>" +
"		</item>" +
"	</result>" +
"	<result>" +
"		<item>" +
"			<name>COLL_NAME</name>" +
"			<value>FNAME</value>" +
"		</item>" +
"		<item>" +
"			<name>DESCRIPTION</name>" +
"			<value>First Name</value>" +
"		</item>" +
"	</result>" +
"</results>";
			XDocument resultsDoc = Xdocument.Parse(inXMLStr);
			var resultsVar = from di in resultsDoc.Descendants("result")
							 select new {
								 ColumnName= (from item in di.Elements("item") where "COLL_NAME".Equals(item.Element("name").Value) select item.Element("value").Value).First(),
								 Description = (from item in di.Elements("item") where "DESCRIPTION".Equals(item.Element("name").Value) select item.Element("value").Value).First()
							 };
			foreach (var item in resultsVar)
			{
				Console.WriteLine("{0} = \"{1}\"", item.ColumnName, item.Description );
			}
			Console.WriteLine("done");
			Console.ReadKey();
		}
	}
}

Now this is much better. The syntax gets a little harry but it works wonderfully. One step closer to being able to unmarshal these stupid objects.
Was This Post Helpful? 0
  • +
  • -

#14 NickDMax  Icon User is offline

  • Can grep dead trees!
  • member icon

Reputation: 2250
  • View blog
  • Posts: 9,245
  • Joined: 18-February 07

Re: LINQ to XML Question

Posted 11 November 2009 - 12:33 PM

View PostlesPaul456, on 11 Nov, 2009 - 02:05 PM, said:

First off, sorry about the confusion. I thought you were just trying to grab the name and value from each child element.

If you told me exaclty what the output needs to be I may be able to give you a better example.

Until then I suggest you look up some samples about of using LINQ-to-XML. It actually is quite powerful and adaptable (I've used it to read WordML files).


I can't really get too specific since we are actually talking about code for work. In general though I am working with webservices that return results of various queries in a format similar to the example one I have given (though in reality it would be much much more complex). I am trying to get familiar with C# and its technologies so that I can deftly create the WS queries (which are in XML) and convert the results into usable objects. I have chosen LINQ-to-XML as the primary technology to investigate because the customer has had success with it in the past and would prefer it to more traditional technologies.

The above is merely an exercise in understanding LINQ and LINQ-to-XML and really has not specific value to anything other than my own understanding.
Was This Post Helpful? 0
  • +
  • -

#15 baavgai  Icon User is online

  • Dreaming Coder
  • member icon

Reputation: 5777
  • View blog
  • Posts: 12,591
  • Joined: 16-October 07

Re: LINQ to XML Question

Posted 11 November 2009 - 02:29 PM

var resultsVar = from di in resultsDoc.Descendants("result")
	select new {
		ColumnName= (from item in di.Elements("item") where "COLL_NAME".Equals(item.Element("name").Value) select item.Element("value").Value).First(),
		Description = (from item in di.Elements("item") where "DESCRIPTION".Equals(item.Element("name").Value) select item.Element("value").Value).First()
	};



I have two issues with this. One is the anonymous Linq type as a return value. However, the biggie is the double dipping. Even with your result set, you still have to do two searches to get the actual values.

Considering the dictionary result of the initial example, I'm inclined to go with key value pairs.

var resultsVar = from di in resultsDoc.Descendants("item")
	where di.Element("name").Value == "COLL_NAME"
	select new KeyValuePair<string, string>(
		di.Element("value").Value,
		(from des in di.Parent.Descendants("item")
		where des != di
		select des.Element("value").Value).First()
		);



The above finds one value absolutely. It then finds the sibling that doesn't match the first. It should be theoretically faster.
Was This Post Helpful? 1
  • +
  • -

  • (2 Pages)
  • +
  • 1
  • 2