Parse xml in C# WP8 - c#

I am relative new with windows phone so I don't know how to parse a document retrieved from the internet.
Now I am able to get it and print in it by console. I want to set each poi retrieved in an instance of my POI class which has all fields from the xml.
By this I print the xml:
XDocument xdoc = XDocument.Parse(e.Result, LoadOptions.None);
System.Diagnostics.Debug.WriteLine(xdoc.FirstNode.ToString());
My class POI has this fields:
Strings: name, description, thumbnal, url; Doubles: lat, lon
And my xml seems like this:
<?xml version="1.0" encoding="utf-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:default="http://www.opengis.net/kml/2.2"
default:ar="http://www.openarml.org/arml/1.0"
default:wikitude="http://www.openarml.org/wikitude/1.0">
<Document>
<ar:provider xmlns:ar="http://www.openarml.org/arml/1.0"
id="myid.com">
<ar:name>Name of the owner</ar:name>
<ar:description><![CDATA[Description of the xml]]></ar:description>
<wikitude:providerUrl xmlns:wikitude="http://www.openarml.org/wikitude/1.0">www.webpage.es</wikitude:providerUrl>
<wikitude:tags xmlns:wikitude="http://www.openarml.org/wikitude/1.0">tag1,tag2,tag3,tag4</wikitude:tags>
<wikitude:logo xmlns:wikitude="http://www.openarml.org/wikitude/1.0">http://mylogourl.png</wikitude:logo>
<wikitude:icon xmlns:wikitude="http://www.openarml.org/wikitude/1.0">www.myiconurl.png</wikitude:icon></ar:provider>
<default:Placemark id="id53">
<ar:provider xmlns:ar="http://www.openarml.org/arml/1.0">myid.com</ar:provider>
<default:name>PLACE</default:name>
<default:description><![CDATA[Description of the place.]]></default:description>
<wikitude:info xmlns:wikitude="http://www.openarml.org/wikitude/1.0">
<wikitude:thumbnail>http://urltothethumbnail/</wikitude:thumbnail>
<wikitude:url>http://urltotheplace.com</wikitude:url>
</wikitude:info>
<default:Point>
<default:coordinates>-3.0000000000,43.0000000000,0</default:coordinates>
</default:Point>
</default:Placemark>
</default:Document>
</default:kml>
I have lots of placemarks (POI), all with the same fields. I want to have an array with all POI retrieved.
I tried with something like this but it says 0 using count:
var pois = xdoc.Root
.Elements("Placemark")
.Select(poi => new POI(
(String)stop.Attribute("name"),
(String)stop.Attribute("description"),
(String)stop.Attribute("thumbnail"),
(String)stop.Attribute("url"),
(Double)stop.Attribute("lat"),
(Double)stop.Attribute("lon")))
.ToList();
If I can get the id of the placemark, it would be useful. I could add another var to my POI class.
EDIT: I tried what one user said in his answer and it crash by giving an exception: System.ArgumentNullException occurred in System.Xml.Linq.ni.dll... I add the modified code:
XDocument xdoc = XDocument.Parse(e.Result, LoadOptions.None);
XNamespace ns = "http://www.opengis.net/kml/2.2";
var placemarks = xdoc.Root.Descendants(ns + "Placemark");
var pois = placemarks
.Select(poi => new POI(
(String)poi.Attribute("name"),
(String)poi.Attribute("description"),
(String)poi.Attribute("thumbnail"),
(String)poi.Attribute("url"),
(Double)poi.Attribute("lat"),
(Double)poi.Attribute("lon")))
.ToList();
System.Diagnostics.Debug.WriteLine(pois.Count);

The following code in your original post:
xdoc.Root.Elements("Placemark")
is not returning any results because the Placemark element is not a root element in your data. Also, you've not specified the namespace. If you want to get all Placemark elements in the document, regardless of where they might be located, then you could do something like this:
XNamespace ns = "http://www.opengis.net/kml/2.2";
var placemarks = xdoc.Root.Descendants(ns + "Placemark");
Your original code will also fail to retrieve the other information (name, description, thumbnail, etc...) because none of that data is actually stored in attributes. Those are all contained in elements, not attributes, and you'll also need to specify the correct namespaces when querying them (see how the namespace was specified in my example above). Finally, note that the latitude, longitude, thumbnail, and url data are contained in sub-elements. You'll have to grab the info and Point elements in order to query those.

Related

Pulling values from individual elements in xml

I am working with a soap response form a wcf service and wish to extract the values form the individual elements. So far I am able to get the list of values from the soap envelope using:
XDocument xDoc = XDocument.Parse(ServiceResult);
List<XElement> ResultsView = xDoc.Descendants()
.Where(x => x.Name.LocalName == "ResultsView")
.ToList();
This gives me the results list:
<a:ResultsView>
<a:Duration>4032</a:Duration>
<a:Metres>41124</a:Metres>
<a:Status>Ok</a:Status>
</a:ResultsView>
I have not been able to get the individual results by querying the ResultsView I can get all the values in a single string which is of no use. Can you suggest a method that will get the values?
The full soap envelope returned is:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><GetLocalDataResponse xmlns="http://tempuri.org/">
<GetLocalDataResult xmlns:a="http://schemas.datacontract.org/2004/07/LocalWcf"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:ResultsView>
<a:Duration>4032</a:Duration>
<a:Metres>41124</a:Metres>
<a:Status>Ok</a:Status>
</a:ResultsView>
</GetLocalDataResult></GetLocalDataResponse></s:Body></s:Envelope>
I have tried a few different methods to extract the values mainly using linq like:
var results = ResultsView.Select(x => new
{
ResultsView = (string)x.Element("Duration"),
duration = x.Element("Duration")
});
The problem is that you're asking for an element without the namespace. If you use the right namespace, you don't need to check for local names or anything like that:
XNamespace ns = "http://schemas.datacontract.org/2004/07/LoacalWcf";
XDocument doc = XDocument.Parse(ServiceResult);
XElement resultsView = doc.Descendants(ns + "ResultsView").Single();
XElement duration = resultsView.Element(ns + "Duration");
Note the use of the + operator to create an XName from an XNamespace and a string.
(It looks like you may well then want to cast duration to int rather than string to get the value in a semantically-useful form.)

Cannot parse xml from Yahoo! Fantasy Sports with c#

I did some searching around the web and could not find the cause of my problem so I apologize if that has already been asked in another form I just did not understand.
My problem is that I am trying to parse the XML retrieved from Yahoo! Fantasy Sports but nothing seems to be working.
I have converted the XML I received (using a GET request with my credentials) into a string. Here it is for evaluation.
<?xml version="1.0" encoding="UTF-8" ?>
- <fantasy_content xml:lang="en-US" yahoo:uri="http://fantasysports.yahooapis.com/fantasy/v2/game/223/players" xmlns:yahoo="http://www.yahooapis.com/v1/base.rng" time="5489.1560077667ms" copyright="Data provided by Yahoo! and STATS, LLC" refresh_rate="60" xmlns="http://fantasysports.yahooapis.com/fantasy/v2/base.rng">
- <game>
<game_key>223</game_key>
<game_id>223</game_id>
<name>Football PLUS</name>
<code>pnfl</code>
<type>full</type>
<url>http://football.fantasysports.yahoo.com/f2</url>
<season>2009</season>
- <players count="25">
- <player>
<player_key>223.p.8261</player_key>
<player_id>8261</player_id>
- <name>
<full>Adrian Peterson</full>
<first>Adrian</first>
<last>Peterson</last>
<ascii_first>Adrian</ascii_first>
<ascii_last>Peterson</ascii_last>
</name>
<editorial_player_key>nfl.p.8261</editorial_player_key>
<editorial_team_key>nfl.t.16</editorial_team_key>
<editorial_team_full_name>Minnesota Vikings</editorial_team_full_name>
<editorial_team_abbr>Min</editorial_team_abbr>
- <bye_weeks>
<week>9</week>
</bye_weeks>
<uniform_number>28</uniform_number>
<display_position>RB</display_position>
- <headshot>
<url>http://l.yimg.com/iu/api/res/1.2/7gLeB7TR77HalMeJv.iDVA--/YXBwaWQ9eXZpZGVvO2NoPTg2MDtjcj0xO2N3PTY1OTtkeD0xO2R5PTE7Zmk9dWxjcm9wO2g9NjA7cT0xMDA7dz00Ng--/http://l.yimg.com/j/assets/i/us/sp/v/nfl/players_l/20120913/8261.jpg</url>
<size>small</size>
</headshot>
<image_url>http://l.yimg.com/iu/api/res/1.2/7gLeB7TR77HalMeJv.iDVA--/YXBwaWQ9eXZpZGVvO2NoPTg2MDtjcj0xO2N3PTY1OTtkeD0xO2R5PTE7Zmk9dWxjcm9wO2g9NjA7cT0xMDA7dz00Ng--/http://l.yimg.com/j/assets/i/us/sp/v/nfl/players_l/20120913/8261.jpg</image_url>
<is_undroppable>1</is_undroppable>
<position_type>O</position_type>
- <eligible_positions>
<position>RB</position>
</eligible_positions>
<has_player_notes>1</has_player_notes>
</player>
- <player>
</players>
</game>
</fantasy_content>
The two methods I have tried are these (PLEASE NOTE: "xmlContent" is the string that contains the XML listed above):
1.)
XDocument chicken = XDocument.Parse(xmlContent);
var menus = from menu in chicken.Descendants("name")
select new
{
ID = menu.Element("name").Value,
};
and
2.)
byte[] encodedString = Encoding.UTF8.GetBytes(xmlContent);
MemoryStream ms = new MemoryStream(encodedString);
XmlDocument doc = new XmlDocument();
doc.Load(ms);
foreach (XmlNode row in doc).SelectNodes("//fantasy_content"))
{}
Basically, I get no results enumerated. I have a feeling I am missing some key steps here though. Any help is greatly appreciated. Thank you all.
UPDATE:
As per the awesome suggestions I received, I tried three more things. Since it is not working still, I did not listen very well. :) Actually, that is semi-accurate, please bear with my newbie attempt at working with XML here as I really am thankful for the responses. Here is how I am screwing up the great suggestions, can you offer another tip on what I missed? Thank you all again.
As per Jon Skeet's suggestion (this yields no results for me):
1.) XNamespace ns = "http://fantasysports.yahooapis.com/fantasy/v2/base.rng";
XDocument chicken = XDocument.Parse(xmlContent);
var menus = from menu in chicken.Descendants(ns + "fantasy_content")
select new
{
ID = menu.Element("name").Value,
};
As per the second suggestion (this throws me an error):
2.) var result = XElement.Load(xmlContent).Descendants().Where(x => x.Name.LocalName == "name");
As per the combinations of suggesting I need to identify the namespace and Yahoo! guide at: http://developer.yahoo.com/dotnet/howto-xml_cs.html
3.) xmlContent = oauth.AcquirePublicData(rtUrl, "GET");
byte[] encodedString = Encoding.UTF8.GetBytes(xmlContent);
MemoryStream ms = new MemoryStream(encodedString);
XmlDocument doc = new XmlDocument();
doc.Load(ms);
XmlNamespaceManager ns = new XmlNamespaceManager(doc.NameTable);
ns.AddNamespace("fantasy_content", "http://fantasysports.yahooapis.com/fantasy/v2/base.rng");
XmlNodeList nodes = doc.SelectNodes("/name", ns);
foreach (XmlNode node in nodes)
{
}
This is what's tripping you up:
xmlns="http://fantasysports.yahooapis.com/fantasy/v2/base.rng"
You're asking for elements in the unnamed namespace - but the elements default to the namespace shown above.
It's easy to fix that in LINQ to XML (and feasible but less simple in XmlDocument)
XNamespace ns = "http://fantasysports.yahooapis.com/fantasy/v2/base.rng";
var menus = chicken.Descendants(ns + "name")
...
Note that in your original method you're actually looking for name elements within the name elements - that's not going to work, but the namespace part is probably enough to get you going.
EDIT: It's not clear why you're using an anonymous type at all, but if you really want all the name element values from the document as ID properties in anonymous type instances, just use:
XNamespace ns = "http://fantasysports.yahooapis.com/fantasy/v2/base.rng";
var menus = chicken.Descendants(ns + "name")
.Select(x => new { ID = x.Value });
Note that there's no need to use Descendants(ns + "fantasy_content") as that's just selecting the root element.
Element name consists of two parts: xmlns(namespace) and localname. If xmlns is absent, name is equal to local name. So, you have to create name with namespace or ignore it
You can ignore namespace in your LINQ, just use LocalName
var result = XElement.Load(#"C:\fantasy_content.xml")
.Descendants()
.Where(x => x.Name.LocalName == "name")
.ToList();

Parsing xml string to get certain tag values within

I have an xml string and have different records within and i want to extract the id within each record. Here is a sample of the xml:
<UploadsInformation >
<Record>
<TaskGUID>48A583CA-A532-419A-9CDB-292764CEC541</TaskGUID>
</Record>
<Record>
<TaskGUID>ED6BA682-2BB2-4ADF-8355-9C605E16E088</TaskGUID>
</Record>
<Record>
<TaskGUID>D20D7042-FC5B-4CF7-9496-D2D9DB68CF52</TaskGUID>
</Record>
<Record>
<TaskGUID>F5DB10C5-D517-4CDA-8AAA-4E3F50B5FF3C</TaskGUID>
</Record>
</UploadsInformation>
This is what i have as a string to extract the information that i need but not sure if it correct or not because when i debug the string seems to be the xml file and not just the specified guid.
string data = new XDocument(new XElement("Record",
uploads.Select(guid => new XElement("TaskGUID", guid.ToString()))))
.ToString();
uploads is: List<Guid?> uploads
If I understand your question correctly, you want to extract the Guids from the source XML, which you indicate is a string.
You can create an XDocument from a string with the following command:
XDocument doc = XDocument.Parse(xmlString);
XNamespace ns = "http://schemas.acatar.com/2013/03/Malt.Models";
List<string> uploads = doc.Descendants(ns + "TaskGUID")
.Select(x => x.Value).ToList();
string uploadString = String.Join(",", uploads);
I used XNamespace because there is a namespace (two, actually) defined in the XML, and unless you prefix the correct one to the element name you won't get any results.
You might be able to combine the last two steps into one line, but I'm not 100% sure.
The above code was tested with your example, and produces the following value for uploadString:
48A583CA-A532-419A-9CDB-292764CEC541,ED6BA682-2BB2-4ADF-8355-9C605E16E088,D20D7042-FC5B-4CF7-9496-D2D9DB68CF52,F5DB10C5-D517-4CDA-8AAA-4E3F50B5FF3C
However, if you're going to loop through the result and pass each one in singularly to a stored procedure, I'd skip the String.Join and just loop through the List:
foreach (string id in uploads)
{
// Do your stored procedure call for each Guid.
}
Added in Response to Comment
In the situation in your comment, if you have a List that you want to get the values for, you'd do essentially the same, but you'll need to check for nulls and (probably) convert the Guid to a string before passing it into the stored proc:
foreach (Guid? g in uploads)
{
if (g != null)
{
string newGuid = g.ToString();
// do your data access stuff here
}
}
You can't use local names of elements, because you have namespace declared. So, you should use namespace to provide names:
XNamespace ns = "http://schemas.acatar.com/2013/03/Malt.Models";
var guids = from r in xdoc.Root.Elements(ns + "Record")
select Guid.Parse((string)r.Element(ns + "TaskGUID"));
Or query your xml without specifying names of elements:
var guids = xdoc.Root.Elements()
.Select(r => Guid.Parse((string)r.Elements().Single()));
I think this is either what you are after or perhaps might shed some light on the direction to go:
string xml = ""; // XML data here
XDocument doc = XDocument.Parse(xml);
List<Guid> guids = doc.Descendants("TaskGUID")
.Select(g => new Guid(g.Value))
.ToList();

Can not get address_component element from Google Geocoding API XML output with LINQ

I try to get some data from Google Geocoding API with C# and ASP.net. But I have trouble with XML responce. I get valid XML and I can get evry element besides "address_component" element. (XML looks like this : http://maps.googleapis.com/...)
/* data is a string with XML from Google server */
XDocument receivedXml = new XDocument();
receivedXml = XDocument.Parse(data);
XName address = XName.Get("address_component");
var root = reciviedXml.Root; //returns corect XElement
XElement result = root.Element("result"); //returns corect XElement
IEnumerable<XElement> components = result.Elements("address_component"); //returns empty collection
This is another way, I'have tried it to with the same result.
var results = reciviedXml.Descendants("address_component");
And when I try to get some descendant of like:
var types = receivedXml.Descendants("type");
It's the empty collection to. But another elements in "result" tag (like the "location" tag)I can get successfully.
Thanks for any advice.
The following:
var receivedXml = XDocument.Load("http://maps.googleapis.com/maps/api/geocode/xml?latlng=49.1962253,16.6071422&sensor=false");
Console.WriteLine(receivedXml.Root.Element("result").Elements("address_component").Count());
Console.WriteLine(receivedXml.Descendants("address_component").Count());
Writes 10 and 28 respectively. Make sure you are using the same instance - in your question you usereceivedXml as well as reciviedXml so you may have two instances XDocument and one of these may contain different data. Also you don't need to instantiate XDocument as XDocument.Parse() will do it for you (and the address variable seems to be unused)

Using LINQ to XML to Process XML in Multiple Namespaces

I'm trying to parse results from the YouTube API. I'm getting the results correctly as a string, but am unable to parse it correctly.
I followed suggestions on a previous thread, but am not getting any results.
My sample code is:
string response = youtubeService.GetSearchResults(search.Term, "published", 1, 50);
XDocument xDoc = XDocument.Parse(response, LoadOptions.SetLineInfo);
var list = xDoc.Descendants("entry").ToList();
var entries = from entry in xDoc.Descendants("entry")
select new
{
Id = entry.Element("id").Value,
Categories = entry.Elements("category").Select(c => c.Value)
//Published = entry.Element("published").Value,
//Title = entry.Element("title").Value,
//AuthorName = entry.Element("author").Element("name").Value,
//Thumnail = entry.Element("media:group").Elements("media:thumnail").ToList().ElementAt(0)
};
foreach (var entry in entries)
{
// entry.Id and entry.Categories available here
}
The problem is that entries has a count of 0 even though the XDocument clearly has the valid values.
The value of the response variable (Sample XML) can be seen here: http://snipt.org/lWm
(FYI: The youTube schema is listed here: http://code.google.com/apis/youtube/2.0/developers_guide_protocol_understanding_video_feeds.html)
Can anyone tell me what I'm doing wrong here?
All the data is in the "http://www.w3.org/2005/Atom" namespace; you need to use this throughout:
XNamespace ns = XNamespace.Get("http://www.w3.org/2005/Atom");
...
from entry in xDoc.Descendants(ns + "entry")
select new
{
Id = entry.Element(ns + "id").Value,
Categories = entry.Elements(ns + "category").Select(c => c.Value)
...
};
etc (untested)
When you see prefix:name, it means that name is in the namespace whose prefix has been declared as prefix. If you look at the top of the document, you'll see an xmlns:media=something. The something is the namespace used for anything with the prefix media.
This means you need to create an XNamespace for each of the namespaces you need to reference:
XNamespace media = XNamespace.Get("http://search.yahoo.com/mrss/");
and then use media for the names in that namespace:
media + "group"
The namespaces in this document are:
xmlns="http://www.w3.org/2005/Atom"
xmlns:app="http://www.w3.org/2007/app"
xmlns:media="http://search.yahoo.com/mrss/"
xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/"
xmlns:gd="http://schemas.google.com/g/2005"
xmlns:gml="http://www.opengis.net/gml"
xmlns:yt="http://gdata.youtube.com/schemas/2007"
xmlns:georss="http://www.georss.org/georss"
You need to set the namespace.
Creating an XName in a Namespace
As with XML, an XName can be in a namespace, or it can be in no namespace.
For C#, the recommended approach for creating an XName in a namespace is to declare the XNamespace object, then use the override of the addition operator.
http://msdn.microsoft.com/en-us/library/system.xml.linq.xname.aspx

Categories