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.)
Related
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");
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
<?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.
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]
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?