Can't parse nested elements - c#

I have this XML that I am parsing and for some reason when I try to pull out the "routes" values, I am only getting the first one in the list. So basically I can extract the "1" value but the rest are being skipped/ignored. I think it's something to do with the nested elements being so far "down" the list but I'm not sure what the problem is.
Any ideas how I can pull out all the route values and not just the first one in the list?
<information xmlns="http://testnamespace" xmlns:i="http://www.w3.org/2001/XMLSchema- instance">
<errorcode>0</errorcode>
<errormessage/>
<numberofresults>1</numberofresults>
<timestamp>12/01/2014 17:20:04</timestamp>
<results>
<result>
<id>1234</id>
<displayid>1234</displayid>
<name>A Road</name>
<name2>Another Road</name2>
<element1>
<element2>
<name>abc</name>
<routes>
<route>1</route>
<route>2</route>
<route>3</route>
<route>4</route>
<route>5</route>
<route>6</route>
</routes>
</element2>
</element1>
</result>
</results>
</information>
This is my code:
foreach (var routeInfo in StopInfo.Descendants(ns + "routes"))
{
string route = routeInfo.Element(ns + "route").Value;
lstResults.Items.Add(route);
}

You are getting only single route element of routes (and it will be first element with value 1):
routeInfo.Element(ns + "route")
And you do same thing for each routes element in you xml. So, if you have only one routes element in your xml, you'll end with single value 1 added to listbox. What you need is getting route elements of selected routes element (they are direct children, so simple Elements() will do the job):
foreach (var route in StopInfo.Descendants(ns + "routes").Elements())
lstResults.Items.Add((string)route);
Also keep in mind that you can simply cast XElement to string and some other types to get their value. That will allow to avoid exception if element is not found and you are trying to read its value.

Because you need more then one route element you should use another loop:
foreach (var routeInfo in StopInfo.Descendants(ns + "routes"))
{
foreach(var route = routeInfo.Elements(ns + "route"))
lstResults.Items.Add((string)route);
}

Although Sergey Berezovskiy has already provided solution but rather we can also use XMLSerializer and annotation for parsing xml in windows phone For reference http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer(v=vs.100).aspx
This might increse number of classes but this is the best way to parse XML data in Windows Phone

Related

How to find a value from a specific element by name?

I have an XML file, that looks like this (only a snippet)
<?xml version="1.0" encoding="UTF-8" ?>
<Message>
<Header>
<Message_Type>RFP</Message_Type>
<Message_Date>2020-11-05T09:36:03+01:00</Message_Date>
<Sequence_Number>225</Sequence_Number>
etc...
I am looking for the fastest way to find the value RFP.
All i know is the elements name that I need to get a value for.
for example, I get names like Message_Type and Message_Date and now I need to get the value for these element names.
There are no attributes in the xml
I did some searching and all I can find is how to find an element with a specific attribute, or all elements with a specific value, stuff like that.
It seems like something very basic but i just cant see how to do it.
I tried something like this
var headerElements = XElement.Load(fileName).Elements("Header");
var element = headerElements.Where(x => x.Element("Message_Type").Name == "Message_Type");
this fills element with the entire Header which seems useless to me. I only need the value of one element in ´Header`, not all
So could some kind soul here put me in the right direction on how to do this ?
Your current query is using a Where call, which will only filter - it doesn't change which elements you're looking at.
What you want is just the Element method itself:
var messageTypeElements = headerElements.Elements("Message_Type");
That will give you all the <Message_Type> elements from all the Header elements.
If in fact you only have a single <Header> and a single <Message_Type> then you can use Element instead:
var message = XElement.Load(fileName);
var header = message.Element("Header");
var messageType = header.Element("Message_Type");
(The Element method will return the first element with the given name, or null if there aren't any. We don't know whether your program should just throw an exception if there are no headers/message_types, or handle it more gracefully.)

Parsing an XML file that can be singleline or multiline

I have an XML file that can be one-line:
<webshop><item></item><item></item></webshop>
or multiline:
<webshop>
<item>
</item>
<item>
</item>
</webshop>
or mixed:
<webshop>
<item></item>
<item></item>
</webshop>
Each tag also has a short variant like <webshop/> and <item/> where the tag is opened and closed in one pair of < > brackets.
each tag can appaer any amount of times, but the <item></item> or <item/> tag will only appaer inside <webshop> ... </webshop>. Also, the entire xml tag hierarchy is much larger then just these two tags (but I kept it simple for this question), and each tag can have attributes.
I'm trying to parse such an xmlfile using an xmlreader in c#, but I always run into a problem.
If I try:
while(reader.ReadToFollowing("webshop"))
{
Console.WriteLine("webshop");
//get attributes of webshop tag and do something...
while(reader.ReadToFollowing("item"))
{
Console.WriteLine("Item");
//get attributes of item tag and do something...
}
}
I never get all the data when the xml is singleline, mixed or the tags close themself (<item/> instead of <item></item>). Most of the time, the reader just stops after one instance of <webshop> or <item>
Is there a robust way to parse this xml, even if the exact lining is not known beforehand? I want to loop over all webshops, and for each webshop loop all over items, and then do something with this data.
Here's a very simple Linq to XML way to read your xml file:
var xml = #"<webshop><item></item><item></item></webshop>";
var reader = XDocument.Parse(xml);
var webshops = from w in reader.Elements("webshop")
select w;
foreach(var shop in webshops)
{
var items = from i in shop.Elements("item")
select i;
//can now grab any attributes of the items
}
Without more details on the attributes in these elements, I can't provide much more detail in an example, but I think this is enough to show you how it can be done.
If you aren't going to do any filtering and just want all of the webshop elements and then their constituent item subelements, you can simplify what I have above like so:
var webshops = reader.Elements("webshop");
foreach(var shop in webshops)
{
var items = shop.Elements("item");
//can now grab any attributes of the items
}
I originally included the more verbose way of structuring the queries in case you wanted to do any filtering or wanted to do something more complex then simply selecting the given elements. This simplified method will produce the same results as my first example.
Please take a look at the answer in this stack overflow discussion.
binding xml elements to model in MVC4
Basically, there are many ways to read xml files in you c# code. It all depend on what you are trying to achieve and how flexible it has to be. I personally prefer to XmlSeriealizer as it translate the xml into c# objects. the only downside is that you have to define classes for the xml to translate into.

Linq duplicate elements when iterating over XML

<?xml version='1.0' encoding='utf-8' standalone='yes'?>
<stock-items>
<stock-item>
<name>Loader 34</name>
<sku>45GH6</sku>
<vendor>HITINANY</vendor>
<useage>Lifter 45 models B to C</useage>
<typeid>01</typeid>
<version>01</version>
<reference>33</reference>
<comments>EOL item. No Re-order</comments>
<traits>
<header>56765</header>
<site>H4</site>
<site>A6</site>
<site>V1</site>
</traits>
<type-validators>
<actions>
<endurance-tester>bake/shake</endurance-tester>
</actions>
<rules>
<results-file>Test-Results.txt</results-file>
<file-must-contain file-name="Test-Results.xml">
<search>
<term>[<![CDATA[<"TEST TYPES 23 & 49 PASSED"/>]]></term>
<search-type>exactMatch</search-type>
</search>
</file-must-contain>
</rules>
</type-validators>
</stock-item>
</stock-items>
Im trying to get the rules fragment from the xml above into a string so it can be added to a database. Currently the search element and its contents are added twice. I know why this is happing but cant figure out how to prevent it.
Heres my code
var Rules = from rules in Type.Descendants("rules")
select rules.Descendants();
StringBuilder RulesString = new StringBuilder();
foreach (var rule in Rules)
{
foreach (var item in rule)
{
RulesString.AppendLine(item.ToString());
}
}
Console.WriteLine(RulesString);
Finally any elements in rules are optional and some of these elements may or may not contain other child elements up to 4 or 5 levels deep. TIA
UPDATE:
To try and make it clearer what im trying to achieve.
From the xml above I should end up with a string containing everthing in the rules element, exactly like this:
<results-file>Test-Results.txt</results-file>
<file-must-contain file-name="Test-Results.xml">
<search>
<term>[<![CDATA[<"TEST TYPES 23 & 49 PASSED"/>]]></term>
<search-type>exactMatch</search-type>
</search>
</file-must-contain>
Objective is to extract the entire contents of the rules element as is while taking account that the rules element may or may not contains child elements several levels deep
If you just want the entirety of the rules element as a string (rather than caring about its contents as xml), you don't need to dig into its contents, you just need to get the element as an XNode and then call ToString() on it :
The following example uses this method to retrieve indented XML.
XElement xmlTree = new XElement("Root",
new XElement("Child1", 1)
);
Console.WriteLine(xmlTree);
This example produces the following output:
<Root>
<Child1>1</Child1>
</Root>
if you want to prevent duplicates than you will need to use Distinct() or GroupBy() after parsing the xml and before building the string.
I'm still not fully understanding exactly what the output should be, so I can't provide a clear solution on what exactly to use, or how, in terms of locating duplicates. If you can refine the original post that would help.
we need the structure of the xml as it would appear in your scenario. nesting and all.
we need an example of the final string.
saving it to a db doesn't really matter for this post so you only need to briefly mention that once, if at all.

How to read XPathNodelist

Programming in C# I got an Xml.XpathNodeList object "ResultsPartRel.nodeList". Debugging it with Visual Studio I can read "Results View ; Expanding the Results View will enumerate the IEnumerable"
Questions:
1.- Which is the best way to read those nodes?
2.- I program the next code but I dont get the expected results. I get the same result twice. (ResultsPartRel.nodeList contains 2 nodes)
List<string> childrenName = new List<string>();
foreach (XmlElement node in ResultsPartRel.nodeList)
{
string nameChildren = node.SelectSingleNode("//related_id/Item/keyed_name").InnerText;
childrenName.Add(nameChildren);
}
Thank you in advance.
EDIT
<related_id>
<Item>
<classification>Component</classification>
<id></id>
<keyed_name>glass</keyed_name> <!-- I want to get this InnerText -->
</Item>
</related_id>
<source_id>968C45A47942454DA9B34245A9F72A8C</source_id>
<itemtype>5E9C5A12CC58413A8670CF4003C57848</itemtype>
Well we really need to see the XML sample and a verbal explanation of which data you want to extract. Currently you do a node.SelectSingleNode(...) so that looks as if you want to select a path relative to node but then you use an absolute path starting with //, that is why you get the same result twice.
So you want node.SelectSingleNode(".//related_id/Item/keyed_name") or perhaps even node.SelectSingleNode("related_id/Item/keyed_name"), depending on the XML you have.
You can get the first element. (With the "//" means search for all following tags, so you will probably get more results).When you want the first element write "//related_id/Item/keyed_name*1*".
Or you can write the exact path.(this is the safest way) To make it easy for yourself there is a Firefox extension xPath Checker load the document in firefox than right click the element and show Xpath. Then you get a exact path.

Using XPath in SelectSingleNode: Retrieving individual element from XML if it's present

My XML looks like :
<?xml version=\"1.0\"?>
<itemSet>
<Item>one</Item>
<Item>two</Item>
<Item>three</Item>
.....maybe more Items here.
</itemSet>
Some of the individual Item may or may not be present. Say I want to retrieve the element <Item>two</Item> if it's present. I've tried the following XPaths (in C#).
XMLNode node = myXMLdoc.SelectSingleNode("/itemSet[Item='two']") --- If Item two is present, then it returns me only the first element one. Maybe this query just points to the first element in itemSet, if it has an Item of value two somewhere as a child. Is this interpretation correct?
So I tried:
XMLNode node = myXMLdoc.SelectSingleNode("/itemSet[Item='two']/Item[1]") --- I read this query as, return me the first <Item> element within itemSet that has value = 'two'. Am I correct?
This still returns only the first element one. What am I doing wrong?
In both the cases, using the siblings I can traverse the child nodes and get to two, but that's not what I am looking at. Also if two is absent then SelectSingleNode returns null. Thus the very fact that I am getting a successfull return node does indicate the presence of element two, so had I wanted a boolean test to chk presence of two, any of the above XPaths would suffice, but I actually the need the full element <Item>two</Item> as my return node.
[My first question here, and my first time working with web programming, so I just learned the above XPaths and related xml stuff on the fly right now from past questions in SO. So be gentle, and let me know if I am a doofus or flouting any community rules. Thanks.]
I think you want:
myXMLdoc.SelectSingleNode("/itemSet/Item[text()='two']")
In other words, you want the Item which has text of two, not the itemSet containing it.
You can also use a single dot to indicate the context node, in your case:
myXMLdoc.SelectSingleNode("/itemSet/Item[.='two']")
EDIT: The difference between . and text() is that . means "this node" effectively, and text() means "all the text node children of this node". In both cases the comparison will be against the "string-value" of the LHS. For an element node, the string-value is "the concatenation of the string-values of all text node descendants of the element node in document order" and for a collection of text nodes, the comparison will check whether any text node is equal to the one you're testing against.
So it doesn't matter when the element content only has a single text node, but suppose we had:
<root>
<item name="first">x<foo/>y</item>
<item name="second">xy<foo/>ab</item>
</root>
Then an XPath expression of "root/item[.='xy']" will match the first item, but "root/item[text()='xy']" will match the second.

Categories