How to read XPathNodelist - c#

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.

Related

Parsing xml tags with colons inside using C#

I'm working in Xamarin on one android app which is parsing xml from this webiste: http://video.cazin.net/rss.php, and populate listview and in particular I have a problem getting value from this tag:
<media:thumbnail url="http://video.cazin.net/uploads/thumbs/2d07f1e49-1.jpg" width="480" height="360"/>
I created namespace:
xmlNameSpaceManager.AddNamespace("ab", "http://search.yahoo.com/mrss/");
and than tried to get value from url attribute:
XmlNodeList xmlNode = document.SelectNodes("rss/channel/item");
if (xmlNode[i].SelectSingleNode("//ab:thumbnail[#url='http://video.cazin.net/rss.php']", xmlNameSpaceManager) != null)
{
var thumbnail = xmlNode[i].SelectSingleNode("//ab:thumbnail=[#url='http://video.cazin.net/rss.php']", xmlNameSpaceManager);
feedItem.Thumbnail = thumbnail.Value;
}
I also tried something like this:
//ab:thumbnail/#url
but than I got value of just first image. I'm sure the problem is here somewhere because I have the same code parisng images from another xml tag without colon inside and it's working correctly. Does anyone had similar experience and knows what I should put in those braces? Thanks
Your current query is searching for a thumbnail element where the url attribute is equal to http://video.cazin.net/rss.php - there are none that match this.
Your 'I also tried' query of //ab:thumbnail/#url is closer, but the // means that the query will start from the root of the document, so you get the all urls (but you only take the first).
If you want the element that matches taking the current node context into consideration, you need to include the current node context in the query - this is represented by .. So .//ab:thumbnail/#url would find all url attributes in a thumbnail element contained by the current node. You can see the result in this fiddle.
I would strongly suggest you use LINQ to XML instead, however. It's a lot nicer to work with than the old XmlDocument API. For example, you could find all item thumbnail urls using this code:
var doc = XDocument.Load("http://video.cazin.net/rss.php");
XNamespace media = "http://search.yahoo.com/mrss/";
var thumbnailUrls = doc.Descendants("item")
.Descendants(media + "thumbnail")
.Attributes("url");

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 figure out the Nth node of something that I currently am in using XPATh

Ok. I have an attribute in an xml document that I know will occur more than once. Using C# I loop through all the nodes that have this attribute. I know how to count the occurrence of an element using xpath...
count("//x/y#b")
and so on.
But is there a way that I can get the n-th value of a node that I am on... for example
<?xml version="1.0"?>
<x>
<y/>
<y/>
<y/>
</x>
Let's say I was looping through that programatically using c#. And lets say I was on the second element. Is there any way using xpath that I could figure out that I am on the 2nd node? I guess I am just trying to find my position in the iteration. Any ideas? Currently scouring the internet. If I find it out I will be sure to let you know.
Thanks.
UPDATE: CAN'T SEEM to get my stuff to work
Ok. I thought I would update my question. I can't seem to get any of your suggestions working...
<Template>
<TemplateData>
<ACOData>
<POPULATION_PATIENT_ID>6161</POPULATION_PATIENT_ID>
<PATIENT_ID>4329</PATIENT_ID>
</ACOData>
<ACOData>
<POPULATION_PATIENT_ID>5561</POPULATION_PATIENT_ID>
<PATIENT_ID>4327</PATIENT_ID>
</ACOData>
<ACOData>
<POPULATION_PATIENT_ID>6160</POPULATION_PATIENT_ID>
<PATIENT_ID>4321</PATIENT_ID>
</ACOData>
<ACOData>
<POPULATION_PATIENT_ID>5561</POPULATION_PATIENT_ID>
<PATIENT_ID>4320</PATIENT_ID>
</ACOData>
That is the XML that I am using. But I can't seem to get the correct count. I am always coming up with zero?
encounter = Int32.Parse((patElm.CreateNavigator().Evaluate("count(/Template/TemplateData/ACOData/POPULATION_PATIENT_ID[.='" + populationPatID + "']/preceding-sibling::ACOData/POPULATION_PATIENT_ID[.='"+populationPatID+"'])")).ToString());
The above is the code that I am attempting to use to get the correct value... Note my count function
count(/Template/TemplateData/ACOData/POPULATION_PATIENT_ID[.='" + populationPatID + "']/preceding-sibling::ACOData/POPULATION_PATIENT_ID[.='"+populationPatID+"'])"
To get the second such element in the document use:
(//x/y[#b])[2]
Suppose you want to go the other way. That is, you have one of these nodes and you want to know its overall position. In general, for any expression <expr> the following is true:
$n = count((<expr>)[$n]/preceding::*[count(.|<expr>)=count(<expr>)])
That is, the position of the Nth element selected by <expr> can be found by counting all the preceding elements also selected by that expression. Using similar techniques, we can find the position of some node that would be selected by a more general expression, within the set of all nodes selected by that expression.
For example, suppose we have the following document:
<x>
<y b="true"/>
<y b="true"/>
<y/>
<y/>
<x><y b="true"/><y/><y b="true">77</y></x>
<y/>
<y/>
</x>
And we want to know the position in the document of the node at /*/*/y[.='77'] among all nodes selected by //x/y[#b]. Then use the following expression:
count(/*/*/y[.='77']/preceding::*[count(.|//x/y[#b])=count(//x/y[#b])]) + 1
A more specific one-off solution looks like this:
count(/*/*/y[.='77']/preceding::y[parent::x and #b]) + 1
Result (in both cases):
4
Note: It's assumed that /*/*/y[.='77'] and (<expr>)[$n] above actually select some node in the document. If not, the result will be an erroneous 1 due to adding 1 to the result of the count. For this reason, this method is probably most useful when working on a context node or when it is guaranteed that your initial expression selects a node. (Of course, initial error checking can be employed, as well.)
Let's say I was looping through that programatically using c#. And
lets say I was on the second element. Is there any way using xpath
that I could figure out that I am on the 2nd node?
Suppose, as you say, that the current (initial context) node is /x/y[2] and you want to see what is its "position".
Evaluate this XPath expression (off the current node):
count(preceding-sibling::y) + 1
You can use the position function
x/y[position() = 3]

How can I parse the information from this XML?

this is an example of the XML I want to scrape:
http://www.dreamincode.net/forums/xml.php?showuser=335389
Notice that the contactinformation tag has many contact elements, each similar but with different values.
For example, the element that has the AIM content in it, how can I get the content of the Value tag that's in the same family as the AIM content element?
That's where I'm stuck. Thanks!
Basically: I need to find the AIM content tag, make a note of where it is, and find the Value element within that same family. Hope this makes the question clearer
LINQToXML
var doc = XDocument.Load(#"http://www.dreamincode.net/forums/xml.php?showuser=335389");
var aimElements = doc.Descendants("contact").Where(a=>a.Element("title").Value == "AIM").Select(a=>a.Element("value").Value);
this will give you a list of strings that hold the value of the value element for a contact that has the title AIM, you can do a First() or a FirstOrDefault if you believe there should only be 1
Using an xpath like the one below will get you the contact/value node where contact/title is "AIM":
/ipb/profile/contactinformation/contact[title='AIM']/value
Have you tried to parse the XML rather than "scraping" it?

Categories