Intersect 2 Xml Files with XDocument in C# - 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).

Related

Deserialize key/value xml into an object

Having some difficulties deserializing the following xml to an object in the most efficient/cleanest possible way:
<item xsi:type="ns2:Map" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<item>
<key xsi:type="xsd:string">mailinglistid</key>
<value xsi:type="xsd:string">1</value>
</item>
<item>
<key xsi:type="xsd:string">uniqueid</key>
<value xsi:type="xsd:string">1a0d0d2195</value>
</item>
<item>
<key xsi:type="xsd:string">name</key>
<value xsi:type="xsd:string">OSM NL</value>
</item>
</item>
This is a single item along with its properties. As you can see the properties are defined as key/value pairs.
Because they are key value pairs i could not find a way to use the Xml deserialize attributes (as described in this question: How to deserialize xml to object )
Therefore i have created the following alternative:
// Retrieves all the elements from the "return" node in the Mailinglist_allResponse node.
var items = response.ResponseFromServer.Descendants(_engineResponseNamespace + "Mailinglist_allResponse").Descendants("return").Elements();
foreach (var item in items)
{
var values = item.Descendants("item").ToArray();
var list = new EngineMailingList
{
MailingListId =
values.Descendants("key")
.First(v => v.Value == "mailinglistid")
.ElementsAfterSelf("value")
.First()
.Value,
UniqueId =
values.Descendants("key")
.First(v => v.Value == "uniqueid")
.ElementsAfterSelf("value")
.First()
.Value,
Name =
values.Descendants("key")
.First(v => v.Value == "name")
.ElementsAfterSelf("value")
.First()
.Value,
};
result.Add(list);
As you can see this is alot more code than when using the deserialize attributes. Is there any way i could still use these attributes so that my code could be cleaner/ efficient? i am going to have to make alot of these functions otherwise.
I don't think attributes will work because of key/value structure. There is no way for a program to infer from the xml alone what properties an object has. I would make a static extension method helper function to get the values:
public static string GetValue(this XElement element, string key){
return values.Descendants("key")
.First(v => v.Value == key)
.ElementsAfterSelf("value")
.First()
.Value;
}
That way your code could look like this:
MailingListId = values.GetValue("mailinglistid"),
UniqueId = values.GetValue("uniqueid"),
Name = values.GetValue("name")

How to correctly perform Linq-to-XML query?

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`,

xml root element missing

I have this xml that i am reading from a url which when i viewsource looks like this:
<xml>
<root>
<item>
<id>1</id>
<name>Testing</name>
</item>
<item>
<id>2</id>
<name>Testing2</name>
</item>
</root>
</xml>
when i ran the code below it keep saying root element is missing? i do have a root element.
public void myfunction()
{
WebRequest request = WebRequest.Create("http://www.site.com/file.xml");
WebResponse response = request.GetResponse();
Stream dataStream = response.GetResponseStream();
string[] arr = XDocument.Load(dataStream).Root.Descendants("Name").Elements().Select(element => element.Value).ToArray(); //error says root element missing
foreach (var item in arr)
{
MessageBox.Show(item.ToString());
}
}
There is no Name element in your xml file. Xml is case-sensitive. You should use lower-case name:
string[] arr = XDocument.Load(dataStream).Root
.Descendants("name")
.Select(name => (string)name)
.ToArray();
BTW your name elements do not have nested elements.
UPDATE: If you want to get values of name elements, then simply cast these elements to string. You can also use Select(name => name.Value) here. Just don't try to get nested elements of name - they don't have any.

xml XPathSelectElements => to string type

I have the following XML structure:
<?xml version="1.0" encoding="utf-8"?>
<xml>
<root>
<Item>
<taxids>
<string>330</string>
<string>374</string>
<string>723</string>
<string>1087</string>
<string>1118</string>
<string>1121</string>
</taxids>
</Item>
</root>
</xml>
I need to get all the string nodes from the xml file to a string variable, like this:
var query = from ip in doc.XPathSelectElements("xml/root/Item")
where ip.XPathSelectElement("taxid").Value == "723"
select ip.XPathSelectElements("taxids").ToString();
But I am getting the following in one row of the variable query:
"System.Xml.XPath.XPathEvaluator+<EvaluateIterator>d__0`1[System.Xml.Linq.XElement]"
I want to get a string like this:
<taxids><string>330</string><string>374</string><string>723</string><string>1087</string><string>1118</string><string>1121</string></taxids>
Is this possible?
Thanks!
I would suggest you something like:
var values = from ids in doc.XPathSelectElements("/xml/root/Item/taxids")
from id in ids.XPathSelectElements("string")
where id.Value.Contains("723")
select ids.ToString();
var result = string.Join("", values);
The value variable will contain all the taxids, which have at least one string child with value 723.
Another variant, which doesn't use XPath for the children checking:
var values = from ids in doc.XPathSelectElements("/xml/root/Item/taxids")
from id in ids.Elements("string")
where id.Value.Contains("723")
select ids.ToString();
var result = string.Join("\n", values);
var doc = XDocument.Parse(#"<?xml version=""1.0"" encoding=""utf-8""?>
<xml>
<root>
<Item>
<taxids>
<string>330</string>
<string>374</string>
<string>723</string>
<string>1087</string>
<string>1118</string>
<string>1121</string>
</taxids>
</Item>
</root>
</xml>");
var query = doc.XPathSelectElement("xml/root/Item/taxids");
Console.WriteLine(query);

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;

Categories

Resources