ask for a specific node in xml with LINQ

M

Michael Soza

supposed we have a xml like this
<root>
<one>
<lat>
</one>
<dos>
<tres>
<cuatro>
<lat></lat>
</cuatro>
</tres>
</dos>
</root>


using linq I want to access to the XElement lat, that is inside of node
"cuatro".
I have this
XElement latnode = (from x in Document.Descendants("dos")
from y in x.Descendants("tres")
from z in y.Descendants("cuatro")
select z.Element("lat")).Single()
is ugly but works, but what happened if they are no 4 levels deep, instead
are 10 levels or more..
is there a better way to do this?
in the XName field can I put something like "dos/tres/cuatro/lat" and select
immediately the node?
im trying but doesn't work.
with Xpath can I do something like im asking but I want to do this in LINQ

Michael Soza
Estudiante Ingeniería Civil Informática
Universidad de Concepción
 
M

Martin Honnen

Michael said:
supposed we have a xml like this
<root>
<one>
<lat>
</one>
<dos>
<tres>
<cuatro>
<lat></lat>
</cuatro>
</tres>
</dos>
</root>


using linq I want to access to the XElement lat, that is inside of node
"cuatro".
I have this
XElement latnode = (from x in Document.Descendants("dos")
from y in x.Descendants("tres")
from z in y.Descendants("cuatro")
select z.Element("lat")).Single()
is ugly but works, but what happened if they are no 4 levels deep,
instead are 10 levels or more..
is there a better way to do this?

Well first of all you could (with the sample you have) use Elements()
instead of Descendants() and instead of all those "from" it suffices to
use e.g.
XElement latnode =
(from x in
Document.Root.Elements("dos").Elements("tres").Elements("cuatro")
select x.Element("lat")).Single();


And of course if the element names at the different levels are unique
(i.e. you don't have nesting of elements with the same name) then
XElement latnode =
(from x in
Document.Descendants("cuatro")
select x.Element("lat")).Single();
would suffice.
in the XName field can I put something like "dos/tres/cuatro/lat" and
select immediately the node?
im trying but doesn't work.
with Xpath can I do something like im asking but I want to do this in LINQ

LINQ to XML also supports XPath with methods like
XPathSelectElements
http://msdn.microsoft.com/en-us/library/system.xml.linq.xdocument.xpathselectelements.aspx
 
M

Michael Soza

thanks, I knew that I can use
Element("something").Element("something2").Element("something3")....etc,etc.
but this is also too verbose. The XPathSelectElements is a nice solution,
but in this forum:
http://social.msdn.microsoft.com/Fo.../thread/c1584d65-11a3-492c-991c-b317e259c60c/
I read that the use of this solution is "rather slow" and that worried me a
little. In the same thread Pablo Straub use a elegant alternative, create a
couple of extension methods that handle the search of the nodes.
Right now I'm using this extensions because I don't want to mess up with
Xpath queries syntax.
And I repeat all the credits for "Pablo Straub" for this solucion :).
namespace NiceExtensions
{
/// <summary>
/// XmlLinqPathExtension implements a very simple relative path to
locate deeply nested sub-elements
/// of an XContainer. The advantage of DeepElements and DeepElements
over XPath's XPathSelectElements
/// and XPathSelectElementit is efficiency, because this solution does
much less work than XPath.
/// The disadvantage is that this is a very limited solution. Should you
need more power, use XPath by
/// replacing calls to DeepElements and DeepElement with SelectElements
and XPathSelectElement, respectively.
/// </summary>

static class XmlLinqSimplePathExtensions
{
/// <summary>
/// Returns an element within an XContainer found deep in the tree,
following a list of element names to
/// traverse.
/// </summary>
/// <param name="xcontainer">XDocument or XElement object to
analyze</param>
/// <param name="path">list of element names</param>
/// <returns>subelement with corresponding path</returns>

public static XElement DeepElement(this XContainer xcontainer,
params XName[] path)
{
if (path.Length == 1)
return xcontainer.Element(path[0]);
else
return xcontainer.Element(path[0]).DeepElement(path.rest());
}

/// <summary>
/// Returns a collection of elements within an XContainer found deep
in the tree, following a list of
/// element names to traverse.
/// </summary>
/// <param name="xcont">XDocument or XElement object to
analyze</param>
/// <param name="path">list of element names</param>
/// <returns>all subelements that match the path starting from
xcont</returns>

public static IEnumerable<XElement> DeepElements(this XContainer
xcont, params XName[] path)
{
if (path.Length == 1)
return xcont.Elements(path[0]);
else
return xcont.Elements(path[0]).DeepElements(path.rest());
}

/// <summary>
/// Returns a collection of elements within a collection of
XElements found deep in the tree, following a
/// list of element names to traverse.
/// </summary>
/// <param name="elems">collection of XElement objects to
analyze</param>
/// <param name="path">list of element names</param>
/// <returns>all subelements that match the path starting from any
element within elems</returns>
///

public static IEnumerable<XElement> DeepElements(this
IEnumerable<XElement> elems, params XName[] path)
{
if (path.Length == 1)
return elems.Elements(path[0]);
else
return elems.Elements(path[0]).DeepElements(path.rest());
}

/// <summary>
/// Returns a copy of an array excluding the first element.
/// </summary>
/// <typeparam name="T">Base type of the array.</typeparam>
/// <param name="source">Source array with at least 2
elements.</param>
/// <returns>A copy of the source, excluding the first
element.</returns>

static T[] rest<T>(this T[] s)
{
T[] r = new T[s.Length - 1];
for (int i = 1; i < s.Length; i++)
r[i - 1] = s;
return r;
}

public static IEnumerable<XElement> DeepElements(this XNode nodo,
string expression)
{
var lala = new XElement("hola");
yield return lala;
}
public static string pico(this string algo)
{
return "asd";
}
}
}
 
M

Michael Soza

heheh the last 2 methods doesn't mean anything, just deleted that. I was
having problems with the inclusion of the extensions, so I wrote a couple of
methods to see what happened, and the error was a typo in the name of the
namespace xD.

Michael Soza said:
thanks, I knew that I can use
Element("something").Element("something2").Element("something3")....etc,etc.
but this is also too verbose. The XPathSelectElements is a nice solution,
but in this forum:
http://social.msdn.microsoft.com/Fo.../thread/c1584d65-11a3-492c-991c-b317e259c60c/
I read that the use of this solution is "rather slow" and that worried me
a little. In the same thread Pablo Straub use a elegant alternative,
create a couple of extension methods that handle the search of the nodes.
Right now I'm using this extensions because I don't want to mess up with
Xpath queries syntax.
And I repeat all the credits for "Pablo Straub" for this solucion :).
namespace NiceExtensions
{
/// <summary>
/// XmlLinqPathExtension implements a very simple relative path to
locate deeply nested sub-elements
/// of an XContainer. The advantage of DeepElements and DeepElements
over XPath's XPathSelectElements
/// and XPathSelectElementit is efficiency, because this solution does
much less work than XPath.
/// The disadvantage is that this is a very limited solution. Should
you need more power, use XPath by
/// replacing calls to DeepElements and DeepElement with SelectElements
and XPathSelectElement, respectively.
/// </summary>

static class XmlLinqSimplePathExtensions
{
/// <summary>
/// Returns an element within an XContainer found deep in the tree,
following a list of element names to
/// traverse.
/// </summary>
/// <param name="xcontainer">XDocument or XElement object to
analyze</param>
/// <param name="path">list of element names</param>
/// <returns>subelement with corresponding path</returns>

public static XElement DeepElement(this XContainer xcontainer,
params XName[] path)
{
if (path.Length == 1)
return xcontainer.Element(path[0]);
else
return
xcontainer.Element(path[0]).DeepElement(path.rest());
}

/// <summary>
/// Returns a collection of elements within an XContainer found
deep in the tree, following a list of
/// element names to traverse.
/// </summary>
/// <param name="xcont">XDocument or XElement object to
analyze</param>
/// <param name="path">list of element names</param>
/// <returns>all subelements that match the path starting from
xcont</returns>

public static IEnumerable<XElement> DeepElements(this XContainer
xcont, params XName[] path)
{
if (path.Length == 1)
return xcont.Elements(path[0]);
else
return xcont.Elements(path[0]).DeepElements(path.rest());
}

/// <summary>
/// Returns a collection of elements within a collection of
XElements found deep in the tree, following a
/// list of element names to traverse.
/// </summary>
/// <param name="elems">collection of XElement objects to
analyze</param>
/// <param name="path">list of element names</param>
/// <returns>all subelements that match the path starting from any
element within elems</returns>
///

public static IEnumerable<XElement> DeepElements(this
IEnumerable<XElement> elems, params XName[] path)
{
if (path.Length == 1)
return elems.Elements(path[0]);
else
return elems.Elements(path[0]).DeepElements(path.rest());
}

/// <summary>
/// Returns a copy of an array excluding the first element.
/// </summary>
/// <typeparam name="T">Base type of the array.</typeparam>
/// <param name="source">Source array with at least 2
elements.</param>
/// <returns>A copy of the source, excluding the first
element.</returns>

static T[] rest<T>(this T[] s)
{
T[] r = new T[s.Length - 1];
for (int i = 1; i < s.Length; i++)
r[i - 1] = s;
return r;
}

public static IEnumerable<XElement> DeepElements(this XNode nodo,
string expression)
{
var lala = new XElement("hola");
yield return lala;
}
public static string pico(this string algo)
{
return "asd";
}
}
}
 
Top