How to correctly perform Linq-to-XML query? - c#

I have a XDocument called currentIndex like that:
<INDEX>
<SUBINDEX>
<!-- Many tag and infos -->
<SUBINDEX>
<ITEM>
<IDITEM>1</IDITEM>
<ITEM>
<ITEM>
<IDITEM>2</IDITEM>
<ITEM>
...
<ITEM>
<IDITEM>n</IDITEM>
<ITEM>
</INDEX>
I would recreate a new XDocument similar to above one:
<INDEX>
<SUBINDEX>
<!-- Many tag and infos -->
<SUBINDEX>
<ITEM>
<IDITEM>2</IDITEM>
<ITEM>
</INDEX>
I want to do this in C#, I have tried starting in this way:
public void ParseItems(XDocument items)
{
IEnumerable<XElement> items = from a in indexGenerale.Descendants(XName.Get("ITEM"))
// where a.Element("IDITEM").Equals("2")
select a;
foreach(var item in items) {
// do something
}
}
Now the problem: If where clause is commented, items contains n elements (one for each ITEM tag), but if I remove that comments items is empty. Why this behaviour. How I need to perform a search?

Use an explicit cast:
from a in indexGenerale.Descendants("ITEM")
where (string)a.Element("IDITEM") == "2"
a.Element("IDITEM") will return an XElement and it will never be equal to "2".Maybe you meant a.Element("IDITEM").Value.Equals("2"), that will also work but explicit cast is safer.It doesn't throw exception if the element wasn't found`,

Related

Intersect 2 Xml Files with XDocument in C#

I have 2 XML Files and I want to get all the XNodes , which are in both files, only based on their same Attribute "id".
This is how an XML File looks like:
<parameters>
<item id="57">
<länge>8</länge>
<wert>0</wert>
</item>
<item id="4">
<länge>8</länge>
<wert>0</wert>
</item>
<item id="60">
<länge>8</länge>
<wert>0</wert>
</item>
</parameters>
Given a second XML File which looks like this:
<parameters>
<item id="57">
<länge>16</länge>
<wert>10</wert>
</item>
<item id="144">
<länge>16</länge>
<wert>10</wert>
</item>
</parameters>
Now I only want the XNode with the ID=57, since it is available in both files. So the output should look like this:
<item id="57">
<länge>8</länge>
<wert>0</wert>
</item>
I already did intersect both files like this:
aDoc = XDocument.Load(file);
bDoc = XDocument.Load(tmpFile);
intersectionOfFiles = aDoc.Descendants("item")
.Cast<XNode>()
.Intersect(bDoc.Descendants("item")
.Cast<XNode>(), new XNodeEqualityComparer());
This only seems to work, when all the descendant Nodes are the same. If some value is different, it won't work. But I need to get this to work on the same Attributes, the values or the descendants doesn't matter.
I also tried to get to the Attributes and intersect them, but this didn't work either:
intersectionOfFiles = tmpDoc
.Descendants(XName.Get("item"))
.Attributes()
.ToList()
.Intersect(fileDoc.Descendants(XName.Get("item")).Attributes()).ToList();
Am I missing something or is this a completely wrong approach?
Thanks in advance.
You should create your own IEqualityComparer that compares XML attributes you want:
public class EqualityComparerItem : IEqualityComparer<XElement>
{
public bool Equals(XElement x, XElement y)
{
return x.Attribute("id").Value == y.Attribute("id").Value;
}
public int GetHashCode(XElement obj)
{
return obj.Attribute("id").Value.GetHashCode();
}
}
which you would then pass to XML parsing code:
var intersectionOfFiles = aDoc.Root
.Elements("item")
.Intersect(
bDoc.Root
.Elements("item"), new EqualityComparerItem());
I also changed some parts of your XML parsing code (XElement instead of XNode since "item" is XML element and "id" is XML attribute).

Looping through xml elements starting with a certain element

How can I loop through a node's elements but from a specific element innertext?
For example:
<Available>
<Item>
<Value>120</Value>
</Item>
<Item>
<Value>121</Value>
</Item>
<Item>
<Value>122</Value>
</Item>
</Available>
The loop looks like this :
foreach (XmlNode node in document.SelectNodes("Available/Item"))
{
//code
}
How can I start the loop from a certain element?
For example , I want it to loop through the elements starting with the "item" which has the value 121
foreach (XmlNode node in document.SelectNodes("Available/Item").SkipWhile(n => n.Value != 121))
{
//code
}

Convert XML to a list of something

I have a xml file coming from my php site as an api.
This is the xml that is coming back from my php application.
<xml>
<overzicht>
<item>
<sessieID>6</sessieID>
<onderwerp>Vrijwilligers, een uitstervend rasnn</onderwerp>
<omschrijving>Ode aan de vrijwilligers jjj</omschrijving>
<sprekerID>1</sprekerID>
<lokaalID>20</lokaalID>
<themaID>1</themaID>
<typeID>2</typeID>
<periodeID>2</periodeID>
<datum>2012-02-20</datum>
<maximaleInschrijvingen>1</maximaleInschrijvingen>
<spreker>
<sprekerID>1</sprekerID>
<sprekerNaam>Rik Torfs</sprekerNaam>
<loginID>13</loginID>
</spreker>
<lokaal>
<lokaalID>20</lokaalID>
<campusNaam>Malle</campusNaam>
<lokaalOpCampus>W10</lokaalOpCampus>
<typeID>2</typeID>
</lokaal>
</item>
<item>
<sessieID>15</sessieID>
<onderwerp>VPKB</onderwerp>
<omschrijving/>
<sprekerID>6</sprekerID>
<lokaalID>2</lokaalID>
<themaID>1</themaID>
<typeID>1</typeID>
<periodeID>2</periodeID>
<datum>2012-02-20</datum>
<maximaleInschrijvingen>50</maximaleInschrijvingen>
<spreker>
<sprekerID>6</sprekerID>
<sprekerNaam>Dick Wursten</sprekerNaam>
<loginID>18</loginID>
</spreker>
<lokaal>
<lokaalID>2</lokaalID>
<campusNaam>KHK Vorselaar</campusNaam>
<lokaalOpCampus>A102</lokaalOpCampus>
<typeID>1</typeID>
</lokaal>
</item>
...
</overzicht>
</xml>
This is my C# code. I want to get al list of Sessie.
XDocument xmlDoc = XDocument.Parse(e.Result);
List<Sessie> sessies =
(
from item in xmlDoc.Descendants("overzicht")
select new Sessie(
item.Element("onderwerp").Value,
Convert.ToInt32(item.Element("sessieID").Value),
item.Element("omschrijving").Value,
(Spreker)(
new Spreker(
Convert.ToInt32(item.Element("spreker").Element("sprekerID").Value),
item.Element("spreker").Element("sprekernaam").Value)
),
Convert.ToDateTime(item.Element("datum").Value),
Convert.ToInt32(item.Element("maximaleInschrijvingen").Value),
(Lokaal)(
new Lokaal(
Convert.ToInt32(item.Element("lokaal").Element("lokaalID").Value),
item.Element("lokaal").Element("campusNaam").Value,
item.Element("lokaal").Element("lokaalOpCampus").Value)
)
)
).ToList<Sessie>();
I know my code isn't working with this exception.
"NullReferenceException"
There's one fairly obvious problem to start with. Look at the very start of your query:
from item in xmlDoc.Descendants("overzicht")
select new Sessie(item.Element("onderwerp").Value,
...
That will only work if there's an <onderwerp> directly under <overzicht>. There isn't - it's under the <item> element. Perhaps (given the range variable name) you meant:
from item in xmlDoc.Descendants("item")
select new Sessie(item.Element("onderwerp").Value,
...
The query
from item in xmlDoc.Descendants("overzicht")
will return a list of <overzicht> elements. item.Element("onderwerp") does not exist, you are missing the <item> element in between.
Simple fix:
from item in xmlDoc.Descendants("item")

Linq to XML, extracting attributes and elements

I am new to XML and Linq to XML and I just can't find a good guide that explains how to work with it. I have a simple XML string structured as follows
<mainitem>
<items>
<itemdescription>ABC</itemdescription>
<item>
<itemtext>XXX</itemtext>
</item>
<item>
<itemtext>YYY</itemtext>
</item>
<item>
<itemtext>ZZZ</itemtext>
</item>
</items>
<overalldescription>ABCDEFG</overalldescription>
<itemnodes>
<node caption="XXX" image="XXX"></node>
<node caption="YYY" image="YYY"></node>
<node caption="ZZZ" image="ZZZ"></node>
</itemnodes>
</mainitem>
I am using C# code like
var Items = (from xElem in XMLCODEABOVE.Descendants("item")
select new ItemObject
{
ItemObjectStringProperty = xElem.Element("itemtext").Value,
}
);
to extract a list of the itemtext objects for use with my code. Where I need help is in extracting a list of the caption and image attributes of my node elements. I also need the overalldescription and the itemdescription. I have tried every variation of the above code substituting Descendant for Elements, Element for Attribute etc. I know this is probably a basic question but there doesn't seem to be a straight forward guide out there to explain this to a beginner.
To get the captions
// IEnumerable<string>
var captions = from node in doc.Descendants("node")
select node.Attribute("caption").Value;
Or both the captions and image attributes in one shot:
// IEnumerable of the anonymous type
var captions = from node in doc.Descendants("node")
select new {
caption = node.Attribute("caption").Value,
image = node.Attribute("image").Value
};
For the descriptions:
// null ref risk if element doesn't exist
var itemDesc = doc.Descendants("itemdescription").FirstOrDefault().Value;
var overallDesc = doc.Descendants("overalldescription ").FirstOrDefault().Value;

How to get node by name?

It's a simple task, but I can't make it work. Given the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<product>
<item1></item1>
<item2></item2>
<item3></item3>
</product>
I'd like to get all nodes within product. Following two attempts return no nodes, I don't see why:
XDocument meteoDoc = XDocument.Load("data.xml");
foreach (var item in meteoDoc.Descendants("product")) {//...}
foreach (var item in meteoDoc.Descendants().Where(x => x.Name == "product").Nodes()) {//...}
The following, as expected, would return me all nodes:
foreach (var item in meteoDoc.DescendantNodes()) { //...}
Thx for any tipps, I can't see the problem... :-/
Your first attempt is asking for all the Descendants called product. Your second attempt is finding the direct child nodes of all descendants called product.
It's possible that it's looking for descendants within the root element... if you know that the root element is called product, you could just use:
foreach (var item in meteoDoc.Root.Descendants())
(I can't test your existing code to find out exactly why it's not working for you right now, I'm afraid.)
Note that Descendants will only find descendant elements - if you want all descendant nodes, you need the DescendantNodes method.
Use this:
XDocument meteoDoc = XDocument.Load("data.xml");
foreach (var item in meteoDoc.Root.Descendants())
{
// ...
}
Try this:
foreach (var item in meteoDoc.Descendants("product"))
{
foreach (var prod in item.Descendants())
{
// do something
}
}
I have made a few change to xml, in this way the data is more well-formed and i can give you a piece of code that accomplish your task.
static void Main(string[] args)
{
//Data.xml
/*
<?xml version="1.0" encoding="UTF-8"?>
<product>
<item>
<name>test</name>
</item>
<item>
<name>test2</name>
</item>
<item>
<name>test3</name>
</item>
</product>
*/
XDocument meteoDoc = XDocument.Load("data.xml");
var result = from c in meteoDoc.Descendants("item")
select new { Name = c.Element("name").Value };
foreach (var item in result)
{
Console.WriteLine(item.Name);
}

Categories

Resources