IEnumerable<XElement> compare is not same - c#

I read an XML file by following two techniques.
By reading the entire XML using Parse XElement XElement.Parse(File.ReadAllText(xmlfile))
Note: I know I shouldn't have used this technique.
By using Load of XDocument XDocument.Load(xmlfile);
Then I tried creating a list of XElement by the following code snippet. To me, results look same but when I try to compare the two IEnumerable object, they aren't same.
What I am overlooking. Here is the code snippet
// Read the xml db file.
XElement xEle = XElement.Parse(File.ReadAllText(xmlfile));
XDocument xDoc = XDocument.Load(xmlfile);
List<XElement> xElementCollection = xEle.Elements("Map").ToList();
List<XElement> xDocumentCollection = xDoc.Descendants("Map").ToList();
bool bCompare = xElementCollection.Equals(xDocumentCollection);
bCompare results to false, however when I look at the data to both the lists. They look same.

You basically need to go through each element in both lists and compare them to each other by value using the XNode.DeepEquals method.
if (xElementCollection.Count != xDocumentCollection.Count)
{
bCompare = false;
}
else
{
bCompare = true;
for (int x = 0, y = 0;
x < xElementCollection.Count && y < xDocumentCollection.Count; x++, y++)
{
if (!XNode.DeepEquals(xElementCollection[x], xDocumentCollection[y]))
bCompare = false;
}
}

Related

How to read the xml file and write to the dictionary

I really need help. I have an xml file with the following structure
<?xml version="1.0" encoding="UTF-8"?>
<Head xmlns="http://www.sample-package.org">
<Number>748</Number>
<Number_confirm>977</Number_confirm>
<Positions>
<Tare_id>2442</Tare_id>
</Positions>
<Positions>
<Product_id>168813</Product_id>
</Positions>
</Head>
I need add a key and value to the dictionary (N and "Number"), (id and Product_id), but need do this without a linq, like:
//load xml from url
doc.Load(temp);
var root = doc.GetElementsByTagName("Head");
var documents = new List<Dictionary<string, object>>();
for (int i = 0; i <root.Count; i++)
{
for (int j = 0; j < root[i].ChildNodes.Count; j++)
{
var element = root[i].ChildNodes[j];
InfoManager.MessageBox("element:{0}", element.Value);
var document = new Dictionary<string, object>();
document.Add("N", element.Attributes.GetNamedItem("Number"));
document.Add("NC", element.Attributes.GetNamedItem("Number_confirm"));
documents.Add("ID",
element.Attributes.GetNamedItem("Product_id"));
documents.Add(document);
}
}
Now element.Attributes = null, MessageBox shows element empty, i cant see Attributes/Element and does not add all the elements to the dictionary. How can I fix it??
1) The element does not have any value. To get the numbers you are looking for, you will have to use element.InnerText as in:
MessageBox.Show(string.Format("element:{0}", element.InnerText));
2) Then in the document.Add-Statements, you are trying to access the Attributes, but there are no Attributes on your elements.
An example for an attribute would be:
<Number MyAttribute="additionalInfo">67</Number>
3) You have a typo in one of your document.Add-Statements. You accidently typed documents once.
4) In your second loop you loop through all the elements in the root (Head). So you enter the loop 4 times and each time you got a different element, but you try to add all the values in each iteration. You want to actually have a List of KeyValuePairs like the in following:
var root = doc.GetElementsByTagName("Head");
var documents = new List<KeyValuePair<string, object>>();
for (int i = 0; i < root.Count; i++)
{
for (int j = 0; j < root[i].ChildNodes.Count; j++)
{
var element = root[i].ChildNodes[j];
MessageBox.Show(string.Format("element:{0}", element.InnerText));
var document = new KeyValuePair<string, object>(element.Name, element.InnerText);
documents.Add(document);
}
}
Now the dictionary equals to the structure your xml has: An object (Head) has a List of KeyValuePairs ("Number" pairs with "748", "Number_confirm" pairs with "977", etc.)
As your comment suggests, if your xml could look as follow:
<?xml version="1.0" encoding="UTF-8"?>
<Head xmlns="http://www.sample-package.org">
<Number>748</Number>
<Number_confirm>977</Number_confirm>
<Positions> <!-- Note: two numbers in one Positions-element -->
<Tare_id>2442</Tare_id>
<Product_id>168813</Product_id>
</Positions>
</Head>
..then you will have to add another loop to go deeper and change the following:
..
var element = root[i].ChildNodes[j];
MessageBox.Show(string.Format("element:{0}", element.InnerText));
string numbers = string.Empty;
for(int z = 0; z < element.ChildNodes.Count; z++)
{
numbers += element.ChildNodes[z].InnerText + Environment.NewLine;
}
var document = new KeyValuePair<string, object>(element.Name, numbers);
documents.Add(document);
..
Alltough I recommend, that you create a class with the properties like in the xml so you can load the xml internal and print/alter it at your will.
Try to use XmlDocument and convert it into JSON object.
XmlDocument doc = new XmlDocument();
doc.Load(*XML FILE PATH*);
string XML_JSON = JsonConvert.SerializeXmlNode(doc);
dynamic jsonObject = JsonConvert.DeserializeObject(XML_JSON);
var XML_OBJECT = jsonObject["Head"];
JToken number = (JToken) XML_OBJECT["Number"]
JToken numberConfirm = (JToken) XML_OBJECT["Number_confirm"];
JArray positions = (JArray) XML_OBJECT["Positions"];
Then Iterate it the way you wanted to be added in your dictionary.
FYI It doesn't have any attributes in your XML file.
The Xml document you provide has <Head> as the root element, so the foreach loop will always iterate over exactly one element, as well as the list of dictionaries.
But probably that was just a short example. To get what you desire, you can find the elements you want using XPath like this:
doc.Load(temp);
XmlNamespaceManager nsMgr = new XmlNamespaceManager(doc.NameTable);
nsMgr.AddNamespace("pkg", "http://www.sample-package.org");
var root = doc.GetElementsByTagName("Head");
var documents = new List<Dictionary<string, object>>();
for (int i = 0; i < root.Count; i++)
{
var head = root[i];
var document = new Dictionary<string, object>();
document.Add("N", head.SelectSingleNode("/pkg:Head/pkg:Number", nsMgr).InnerText);
document.Add("NC", head.SelectSingleNode("/pkg:Head/pkg:Number_confirm", nsMgr).InnerText);
document.Add("ID", head.SelectSingleNode("/pkg:Head/pkg:Positions/pkg:Product_id", nsMgr).InnerText);
documents.Add(document);
}

Code efficiency for nested loops c#

Here is my function in which I am using nested loops like:
public string HGSearchNew(HolidayFeedService.PackageHolidays.SearchCriteria objsearchcriteria, dynamic search)
{
XmlDocument xdoc = new XmlDocument();
XmlNodeList ndepartures = xdoc.SelectNodes("Destinations/Departure");
string sFinalDeparture = objsearchcriteria.DepartureAirport.ToUpper();
for (int i = 0; i < objsearchcriteria.DepartureAirport.Split('|').Length; i++)
{
for (int j = 0; j < ndepartures.Count; j++)
{
if (objsearchcriteria.DepartureAirport.Split('|')[i].ToUpper() == ndepartures[j]["Name"].InnerText.ToUpper())
{
if (!sFinalDeparture.Contains(objsearchcriteria.DepartureAirport.Split('|')[i].ToUpper()))
sFinalDeparture += objsearchcriteria.DepartureAirport.Split('|')[i].ToUpper() + "|";
break;
}
}
}
return sFinalDeparture;
}
I want to make this code efficient like instead of using loops,use of Contains or Any functions for comparing.
kindly help me out?
I think you can replace the whole method with this readable and efficient LINQ approach:
public string HGSearchNew(HolidayFeedService.PackageHolidays.SearchCriteria objsearchcriteria)
{
XmlDocument xdoc = new XmlDocument();
XmlNodeList ndepartures = xdoc.SelectNodes("Destinations/Departure");
string[] departureTokens = objsearchcriteria.DepartureAirport.Split('|');
var matches = ndepartures.Cast<XmlNode>()
.Select(node => node.Name)
.Intersect(departureTokens, StringComparer.InvariantCultureIgnoreCase);
return string.Join("|", matches);
}

XmlReader parsing single line of xml

I have an xml file, even though it's not in the correct format. I just want to retrieve the values since that is my main goal. Also the file is one single line. The file looks something like this.
<Real64List>...numbers value....</Real64List> <Real64List>...numbers value...</Real64List><Uint32List>...numbers value..</Uint32List>.
I was able to first the first part of , but when I'm trying to parse the second part and third part I get an error "multiple root on line 1". So how can I parse the second part of Real64List and Uint32List. Thanks in advance!
Here is the snippet of the code I'm using.
using (XmlReader reader = XmlReader.Create("generator_0000001.xml"))
{
List<float> vertex = new List<float>();
List<Vector3> verticesList = new List<Vector3>();
while (!reader.EOF)
{
if (reader.NodeType == XmlNodeType.Element && reader.Name == "Real64List")
{
string xmlValue = reader.Value;
string[] coordinates = xmlValue.Split(',');
for (int i = 0; i < coordinates.Length; i++)
{
vertex.Add(float.Parse(coordinates[i]));
}
for (int i = 0; i < vertex.Count; i += 3)
{
var myVect = new Vector3(vertex[i], vertex[i + 1], vertex[i + 2]);
verticesList.Add(myVect);
}
}
}
You could insert those XmlElements into a new XML node. Then you should be able to parse it as valid XML. Some straightforward examples can be found on MSDN

How to read this part of the web XML file?

I am working on a XML reader which shows the result in the labels.
I want to read the node called "Opmerking" which is standing in "Opmerkingen"
A example:
<VertrekkendeTrein>
<RitNummer>4085</RitNummer>
<VertrekTijd>2014-06-13T22:00:00+0200</VertrekTijd>
<EindBestemming>Rotterdam Centraal</EindBestemming>
<TreinSoort>Sprinter</TreinSoort>
<RouteTekst>A'dam Sloterdijk, Amsterdam C., Duivendrecht</RouteTekst>
<Vervoerder>NS</Vervoerder>
<VertrekSpoor wijziging="false">4</VertrekSpoor>
<Opmerkingen>
<Opmerking> Rijdt vandaag niet</Opmerking>
</Opmerkingen>
</VertrekkendeTrein>
"Opmerkingen" is not always there, it is always changing. The code i use now:
XmlNodeList nodeList = xmlDoc.SelectNodes("ActueleVertrekTijden/VertrekkendeTrein/*");
and:
foreach (XmlNode nodelist2 in nodeList)
{
if (i < 17) //4
{
switch (nodelist2.Name)
{
case "VertrekTijd": string kuttijd4 = (nodelist2.InnerText);
var res4 = Regex.Match(kuttijd4, #"\d{1,2}:\d{1,2}").Value;
lblv4.Text = Convert.ToString(res4); break;
case "TreinSoort": lblts4.Text = (nodelist2.InnerText); break;
case "RouteTekst": lblvia4.Text = (nodelist2.InnerText); break;
case "VertrekSpoor": lbls4.Text = (nodelist2.InnerText); i++; break;
}
}
}
How can i read the part "Opmerking" and set it in a case?
I tried it a few times, but it failed.
i also tried:
case "Opmerking": var texeliseeneiland1 = (nodelist2.InnerText); if (texeliseeneiland1 == null) { } else { lblop1.Text = texeliseeneiland1; lblop1.Font = new Font(lblop1.Font.FontFamily, 17); lblop1.Visible = true; picop1.Visible = true; }; break;
Anyone who knows the answer?
Just extend your logic with check whether current node has child nodes and if so, read them and process:
if (nodelist2.HasChildNodes)
{
for (int i=0; i<nodelist2.ChildNodes.Count; i++)
{
var childNode = root.ChildNodes[i];
//do whatever you need to display the contents of the child node.
}
}
Also I have to recommend to consider LinqToXML or at least refactor the code you shared. With LinqToXML is might be as easy as this:
var temp = from remarkNode in nodelist2.Descendants("Opmerking")
select remarkNode.Value;
Somehow load the xml content in an XDocument object and loop through it.
Example: read it from a file
var doc = XDocument.Load("C:/test.xml");
foreach (var xe in doc.Descendants("Opmerking"))
{
var value = xe.Value;
//Do your job with value
}

Unable to access inner Xml elements

Question Background:
I have extracted the following inner XML from a larger document:
<Counters total="1" executed="1" passed="1" error="0" failed="0" timeout="0" aborted="0" inconclusive="0" passedButRunAborted="0" notRunnable="0" notExecuted="0" disconnected="0" warning="0" completed="0" inProgress="0" pending="0" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010" />
Issue:
Using the following code, I have attempted to access each of the elements of the above XML. I need to extract both the name i.e 'total' and its value '1';
XmlDocument innerXmlDoc = new XmlDocument();
innerXmlDoc.LoadXml(node.InnerXml);
XmlElement element = innerXmlDoc.DocumentElement;
XmlNodeList elements = element.ChildNodes;
for (int i = 0; i < elements.Count; i++)
{
//logic
}
If someone can tell me how to get these values that would be great.
You're iterating though the ChildNodes collection of your element and since the element does not have any, you're iterating through the empty nodelist it gives you.
You want to iterate through the Attributes collection instead:
XmlAttributeCollection coll = element.Attributes;
for (int i = 0; i < coll.Count; i++)
{
Console.WriteLine("name = " + coll[i].Name);
Console.WriteLine("value = " + coll[i].Value);
}
It seems you need a Dictionary. Try using LINQ to XML
var values = new Dictionary<string,string>();
var xmlDocument = XDocument.Load(path);
XNamespace ns = "http://microsoft.com/schemas/VisualStudio/TeamTest/2010";
values = xmlDocument
.Descendants(ns + "Counters")
.SelectMany(x => x.Attributes)
.ToDictionary(x => x.Name, x => (string)x));
Managed to solve this myself:
foreach (XmlNode node in nodes)
{
XmlDocument innerXmlDoc = new XmlDocument();
innerXmlDoc.LoadXml(node.InnerXml);
var list = innerXmlDoc.GetElementsByTagName("Counters");
for (int i = 0; i < list.Count; i++)
{
string val = list[i].Attributes["total"].Value;
}
};

Categories