Using LINQ to XML to Process XML in Multiple Namespaces - c#

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

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.)

Read particular XML

I have this XML:
<palinsesto>
<giorno label="Mer" data="2014/12/31">
<canale description="Premium Cinema" id="KE">
<prg Pod="N" Nettv="N" orafine="06:30" orainizio="06:00" replica="No" primaTv="No">
<durata duratapixel="30">30</durata>
<tipologia>Type</tipologia>
<titolo>evento iniziato ieri</titolo>
<descrizione>--</descrizione>
<audio sottotitoli="No subtitles" audioType="Mono" doppioAudio="One language">Not used</audio>
<parentalRating>LIBERO DA DIVIETI</parentalRating>
<trafficLight/>
<anno>--</anno>
<paese>--</paese>
and i need to read the value in prg class and palinsesto class, i try in this mode but not work
XDocument doc = XDocument.Parse(e.Result);
var canal = doc.Descendants(XName.Get("description", "canale")).FirstOrDefault();
var date = doc.Descendants(XName.Get("data", "giorno")).FirstOrDefault();
var title = doc.Descendants(XName.Get("titolo", "prg")).FirstOrDefault();
return always error
It looks like you've misunderstood names, attributes and elements. It looks like you just want something like:
XDocument doc = XDocument.Parse(e.Result);
var root = doc.Root;
var canal = root.Element("canale").Attribute("description").Value;
var date = root.Element("giorno").Attribute("data").Value;
var title = root.Element("titolo").Value;
However:
Currently none of your first three elements are closed, which would cause the above to fail; it's not clear what your real XML would look like. You should indent it to show the intended structure.
Your date is not represented in the normal way for XML - if you're in control of the XML, it would be better to have a value of 2014-12-31
The above code assumes you just want the first element from the root. If that's not the case, you'll need to give us more information
You do not require this XName overload here - the second parameter is used to provide a namespace, which isn't present in the xml you've provided. I believe you are confusing attributes and xmlns namespaces. In order to obtain the attributes, use .Attributes(), as follows:
var canal = doc.Descendants("canale").Attributes("description").FirstOrDefault();
var date = doc.Descendants("giorno").Attributes("data").FirstOrDefault();
var title = doc.Descendants("prg").Attributes("titolo").FirstOrDefault();

Finding a set of nodes in an XML [duplicate]

Can't get any result in feeds.
feedXML has the correct data.
XDocument feedXML = XDocument.Load(#"http://search.twitter.com/search.atom?q=twitter");
var feeds = from entry in feedXML.Descendants("entry")
select new
{
PublicationDate = entry.Element("published").Value,
Title = entry.Element("title").Value
};
What am I missing?
You need to specify the namespace:
// This is the default namespace within the feed, as specified
// xmlns="..."
XNamespace ns = "http://www.w3.org/2005/Atom";
var feeds = from entry in feedXML.Descendants(ns + "entry")
...
Namespace handling is beautifully easy in LINQ to XML compared with everything other XML API I've ever used :)
You need to specify a namespace on both the Descendents and Element methods.
XDocument feedXML = XDocument.Load(#"http://search.twitter.com/search.atom?q=twitter");
XNamespace ns = "http://www.w3.org/2005/Atom";
var feeds = from entry in feedXML.Descendants(ns + "entry")
select new
{
PublicationDate = entry.Element(ns + "published").Value,
Title = entry.Element(ns + "title").Value
};
If you look at the XML returned by the HTTP request, you will see that it has an XML namespace defined:
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" ...>
<id>tag:search.twitter.com,2005:search/twitter</id>
...
</feed>
XML is just like C#, if you use an element name with the wrong namespace, it is not considered to be the same element! You need to add the required namepsace to your query:
private static string AtomNamespace = "http://www.w3.org/2005/Atom";
public static XName Entry = XName.Get("entry", AtomNamespace);
public static XName Published = XName.Get("published", AtomNamespace);
public static XName Title = XName.Get("title", AtomNamespace);
var items = doc.Descendants(AtomConst.Entry)
.Select(entryElement => new FeedItemViewModel()
new {
Title = entryElement.Descendants(AtomConst.Title).Single().Value,
...
});
The issue is in feedXML.Descendants("entry"). This is returning 0 results
According to the documentation you need to put in a fully qualified XName

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 complex XML with C#

I am trying to parse a complex XML with C#, I am using Linq to do it. Basically, I am doing a request to a server and I get XML, this is the code:
XElement xdoc = XElement.Parse(e.Result);
this.newsList.ItemsSource =
from item in xdoc.Descendants("item")
select new ArticlesItem
{
//Image = item.Element("image").Element("url").Value,
Title = item.Element("title").Value,
Description = this.Strip(item.Element("description").Value).Substring(0, 200).ToString()
}
And this is the XML structure:
<item>
<test:link_id>1282570</test:link_id>
<test:user>SLAYERTANIC</test:user>
<title>aaa</title>
<description>aaa</description>
</item>
How I can access to the property test:link_id for example?
Thanks!
Currently your XML is invalid since the test namespace is not declared, you can declare it like this:
<item xmlns:test="http://foo.bar">
<test:link_id>1282570</test:link_id>
<test:user>SLAYERTANIC</test:user>
<title>aaa</title>
<description>aaa</description>
</item>
Having this you can use XNamespace to qualify the XML element you want with the correct namespace:
XElement xdoc = XElement.Parse(e.Result);
XNamespace test = "http://foo.bar";
this.newsList.ItemsSource = from item in xdoc.Descendants("item")
select new ArticlesItem
{
LinkID = item.Element(test + "link_id").Value,
Title = item.Element("title").Value,
Description = this.Strip(item.Element("description").Value).Substring(0, 200).ToString()
}
To write a query on XML that is in a
namespace, you must use XName objects
that have the correct namespace. For
C#, the most common approach is to
initialize an XNamespace using a
string that contains the URI, then use
the addition operator overload to
combine the namespace with the local
name.
To retrieve the value of the link_id element you will need to declare and use an XML namespace for the test:link element.
Since you did not show the namespace declaration in your example XML, I am going to assume it is declared somewhere elese in the XML document. You need to locate the namespace declaration in the XML ( something like xmlns:test="http://schema.example.org" ) which is often declared in the root of the XML document.
After you know this, you can do the following to retrieve the value of the link_id element:
XElement xdoc = XElement.Parse(e.Result);
XNamespace testNamespace = "http://schema.example.org";
this.newsList.ItemsSource = from item in xdoc.Descendants("item")
select new ArticlesItem
{
Title = item.Element("title").Value,
Link = item.Element(testNamespace + "link_id").Value,
Description = this.Strip(item.Element("description").Value).Substring(0, 200).ToString()
}
See the XNamespace and Namespaces in C#, and How to: Write Queries on XML in Namespaces for further information.

Categories