Unable to access inner Xml elements - c#

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;
}
};

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);
}

IEnumerable<XElement> compare is not same

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;
}
}

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);
}

Search through the elements of an xml document in windows phone 8

I have an xml document i am trying to query and link the data to a listbox in windows phone 8 but i have so far failed. the examples online dont seem to bring what exactly i want.
Below is what i have at the moment. in xml.
This is my xml document(mydata.cml)
<resources>
<songCover>
<item>Images/song1</item>
<item>Images/song2</item>
.....
</songCover>
<songName>
<item>song1</item>
<item>song2</item>
.....
</songName>
<releaseYear>
<item>2015</item>
<item>2014</item>
.....
</releaseYear>
and the c#
XDocument doc=XDocument.Load("mydata.xml");
var data1=from Q in doc.Descedants("songCover") select new{Item=Q.Element("item").Value;
var data2=from Q in doc.Descedants("songName") select new{Item=Q.Element("item").Value;
var data3=from Q in doc.Descedants("releaseYear") select new{Item=Q.Element("item").Value;
foreach(var x in data1)
foreach(var y in data2)
foreach(var z in data3)
//create a list of songs with there data eg [songCover1,song1,2015],[songCover2,song2,2014] etc
this is my xaml
<longlistselector>
......
<stackPanel>
<image height=”100” width=”100” source={Binding data1} horizontal-alignment=left/>
<textblock horizontal-alignment=right text={Binding data 2}/>
<texblock horizontal-alignment=right text={Binding data 3}/>
</stackPanel>
......
</longlistselector>
Thank You in advance.
Assuming that number of items in each collection (songCovers, songNames, and releaseYears) are the same, and they corresponds by index, you can do this way :
XDocument doc = XDocument.Load("mydata.xml");
var data1 = doc.Descedants("songCover")
.Elements("item")
.Select(o => o.Value)
.ToList();
var data2 = doc.Descedants("songName")
.Elements("item")
.Select(o => o.Value)
.ToList();
var data3 = doc.Descedants("releaseYear")
.Elements("item")
.Select(o => o.Value)
.ToList();
for(int i=0; i<data1.Count; i++)
{
var songCover = data1[i];
var songName = data2[i];
var releaseYear = data3[i];
//combine the 3 values above as needed
}
Is this what you are looking for?. I am assuming(base on what you wrote in your snippet as comment) covers, names and years are respectively at same position.
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("path_here");
XmlNodeList covers = xmlDoc.SelectNodes("/songCover/item");
XmlNodeList names = xmlDoc.SelectNodes("/songName/item");
XmlNodeList years = xmlDoc.SelectNodes("/releaseYear/item");
for(int i=0;i<covers.Count; i++)
{
Console.WriteLine("[ "+ covers[i].Value +", " + names[i].Value + ", "+ years[i].Value + " ]");
}
XDocument xdoc = XDocument.Load("file_path_here");
List<xElement> covers = xDoc.XPathSelectElements("/songCover/item").ToList();
List<xElement> names = xDoc.XPathSelectElements("/songName/item").ToList();
List<xElement> years = xDoc.XPathSelectElements("/releaseYear/item").ToList();
for (int i = 0; i < covers.Count; i++)
{
Console.WriteLine("[ " + covers[i].Value + ", " + names[i].Value + ", " + years[i].Value + " ]");
}

How to retrieve value from attribute in XML?

What I have:
<?xml version="1.0" encoding="Unicode" standalone="yes"?>
<FiberItems>
<Fiber China="1a" England="1b" Japan="1c" France="1d" Korea="1e"/>
<Fiber China="2a" England="2b" Japan="2c" France="2d" Korea="2e"/>
</FiberItems>
What I want:
1.retrive all the value of "China" into a string array.
2.if a value of China is "1a", retrive all the value of the rest attributes(1b,1c,1d,1e),.
What I do:
1. I write codes for purpose 1 , but never works >_<
XDocument doc = XDocument.Load("/FiberItems.xml");
IEnumerable<string> query = from fiber in doc.Root.Elements("Fiber")
select (string)fiber.Attribute("China");
string[] myString = new string[3];
for (int i = 0; i < 3; i++)
{
foreach (string item in query)
{
myString[i] = (string)item;
}
}
2. for purpose 2, have no idea yet >_<
need helps
You probably shouldn't use an array to collect your data but when you do:
string[] myString = new string[3];
// for (int i = 0; i < 3; i++)
// {
int i = 0;
foreach (string item in query)
{
myString[i] = (string)item;
i += 1;
}
// }
You are doing 3x3 rounds where only 3 are needed.
You can use the following code:
XDocument root = XDocument.Load("/FiberItems.xml");
var attributesChina = root.Elements("FiberItems").Elements("Fiber").Attributes("China");
// attributesChina will contain all the values of china
foreach (XAttribute china in attributesChina)
{
string value = china.value;
}
Check out System.Xml.Linq.
Here's an example to get a list of all the attributes for one element.
var xml = #"<?xml version=""1.0"" encoding=""Unicode"" standalone=""yes""?>
<FiberItems>
<Fiber China=""1a"" England=""1b"" Japan=""1c"" France=""1d"" Korea=""1e""/>
<Fiber China=""2a"" England=""2b"" Japan=""2c"" France=""2d"" Korea=""2e""/>
</FiberItems>";
XDocument doc = XDocument.Parse(xml);
XElement ele = doc.Root.Element("Fiber");
var att = ele.Attributes();
//Load XML element
XElement root = XElement.Parse(File.ReadAllText("/FiberItems.xml"));
//All china attributes (retrieves 1a,2a)
var chinaAttributes= root.Elements().Attributes("China");
//load all rest attributes for china = 1a, loads 1b,1c,1d,1e
var chinaOneARestAttributes = root.Elements().Where(a=>a.Attribute("China")!=null && a.Attribute("China").Value=="1a").Attributes().Select(x=>x.Value).Where(x=>!String.Equals(x,"1a"));
UPDATED for Null Reference Exception. With the data i had tried earlier, i ensured Attribute China was present for all elements.
In XPath:
/FiberItems/Fiber[#China='1a']/#*
gets you all the attributes of Fiber elements with China='1a' and
/FiberItems/Fiber[#China='1a']/#*[local-name()!='China']
gets you the same sequence of attributes excluding the China attribute.
So your requirement is if the attribute China value is "1a" then get all the attribute value of that node. I think it should work
XDocument doc = XDocument.Load("/FiberItems.xml");
IEnumerable<string> query = from fiber in doc.Root.Elements("Fiber")
//condition 1
where (string)fiber.Attribute("China") == "1a"
//condition 2 : Select all except china attribute
select fiber.Attributes().Where(a => a.Name !="China");
Using LINQ to XML:
var xmlStr = #"<?xml version=""1.0"" encoding=""Unicode"" standalone=""yes""?>
<FiberItems>
<Fiber China=""1a"" England=""1b"" Japan=""1c"" France=""1d"" Korea=""1e""/>
<Fiber China=""2a"" England=""2b"" Japan=""2c"" France=""2d"" Korea=""2e""/>
</FiberItems>";
var doc = XDocument.Parse(xmlStr);
var query =
from fiber in doc.Descendants("Fiber")
where (string)fiber.Attribute("China") == "1a"
select String.Join(", ",
(from attr in fiber.Attributes()
where attr.Name != "China"
select (string)attr).ToArray());
This will return a sequence of the other attribute values for each Fiber element that contains a China attribute with the value 1a.

Categories