Issues Querying XML with Namespace - c#

I am attempting to consume a Rest service that returns an XML response. I have successfully made the get request, my problem is processing the response. The response includes a namespace that seems to be messing up my linq query. I have tried almost everything I can think of userNames always comes up empty. Any help would be greatly appreciated and could possibly save my sanity.
<?xml version="1.0" encoding="UTF-8"?>
<tsResponse xmlns="http://tableausoftware.com/api" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://tableausoftware.com/api http://tableausoftware.com/api/ts-api-2.0.xsd">
<users>
<user id="c9274ce9-0daa-4aad-9bd2-3b1d6d402119" name="_DevITTemplate" role="Unlicensed" publish="false" contentAdmin="false" lastLogin="" externalAuthUserId=""/>
string usersList =
request.DownloadString("http://bshagena-sandbox/api/2.0/sites/b4126fe9-d7ee-4083-88f9- a5eea1f40416/users/");
request.Dispose();
XDocument xd;
XElement users;
XNamespace ns = "http://tableausoftware.com/api";
xd = XDocument.Parse(usersList);
users = xd.Root.Elements().First();
var userNames = from a in users.Descendants(ns +"users")
select (string)a.Attribute("name").Value;

It is your user element which contains attribute name, not the names wrapper element. Adjust your xpath accordingly: (Your use of the XNamespace is fine)
var userNames = from a in users.Descendants(ns + "user")
select a.Attribute("name").Value;
Minor - Attribute.Value is already a string - no need to cast it :)

Here is what I did and it seemed to work. Sorry for all the comments. I am sure this is not the most efficient way to do this. I hope this helps someone else losing their mind over the same issue.
// Sends get request and stores response as a string
string usersList =
request.DownloadString("http://<serverName>/api/2.0/sites/b4126fe9-d7ee-4083-88f9-a5eea1f40416/users/");
// declares an XML document object
XDocument xd;
// Declares and XML element object
XElement users;
// Declares a stupid XML namespace object
XNamespace ns = "http://tableausoftware.com/api";
// Sets document to value of string
xd = XDocument.Parse(usersList);
// Sets the element to value of the first node of the xml document
users = xd.Root.Elements().First();
// Creates an array and queries elements based on attribute of name
var userNames = from a in users.Elements(ns + "user")
select (string)a.Attribute("name").Value;

Related

Unable to extract child element from an XML using XDocument

Following is the XML from which I am trying to extract a child element.
<?xml version="1.0" encoding="UTF8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header xmlns="http://SomeValue/SomeValue/2009/01/SomeValue.xsd">
<Session>
<SessionId>SomeValue</SessionId>
<SequenceNumber>1</SequenceNumber>
<SecurityToken>SomeValue</SecurityToken>
</Session>
</soap:Header>
<soap:Body>
<Security_AuthenticateReply xmlns="http://SomeValue/SomeValue">
<processStatus>
<statusCode>P</statusCode>
</processStatus>
</Security_AuthenticateReply>
</soap:Body>
</soap:Envelope>
public static string AssignSecurityToken(string response)
{
string Token = "";
XNamespace ns = "http://schemas.xmlsoap.org/soap/envelope/";
XElement xdoc = XElement.Parse(response);
XElement root = xdoc.Descendants(ns + "Header").First();
Token = root.Element("Session").Element("SecurityToken").Value;
Token = root.Descendants("Session").Descendants().Where(n => n.Name == "SecurityToken").FirstOrDefault().Value;
return Token;
}
I want to extract the element Security Token.
Following are the things that I have already worked on:
Tried extracting the element using the approach suggested in the post
How to get value of child node from XDocument
Also posting some code for reference. Both the statements that are
assigning values to the Token variable are throwing "Object not set
to an instance of an object"exception.
Thanks in advance.
You need to take into account the Header's namepace.
public static string AssignSecurityToken(string response)
{
XNamespace ns1 = "http://schemas.xmlsoap.org/soap/envelope/";
XNamespace ns2 = "http://SomeValue/SomeValue/2009/01/SomeValue.xsd";
var envelope = XElement.Parse(response);
var header = envelope.Element(ns1 + "Header");
var session = header.Element(ns2 + "Session");
var security_token = session.Element(ns2 + "SecurityToken");
return security_token.Value;
}
Actually you could go ahead and just call
return XElement.Parse(response).Descendants()
.First(x => x.Name.LocalName == "SecurityToken").Value;
For this response only, it makes sense to just parse the string and extract an element.
This response uses two namespaces, one for SOAP headers and another for the Amadeus login response. You need the second one to retrieve the token :
//SOAP-only namespace
XNamespace soap = "http://schemas.xmlsoap.org/soap/envelope/";
//Default namespace
XNamespace ns = "http://SomeValue/SomeValue/2009/01/SomeValue.xsd";
var response=XElement.Parse(xml);
var token=response.Descendants(ns+"SecurityToken").First().Value;
Other Amadeus responses are huge and XDocument won't be much better (if at all) than using WCF and deserializing to strongly typed objects. XDocument deserializes the entire XML response, the same as DataContractSerializer. Instead of getting back a strongly-typed set of objects though, you get XElements you'll have to map to something else.
If you want to reduce memory consumption by only reading the parts you'll have to use XmlReader and read the XML tokens from the response stream one by one. That's a lot more work.
Another interesting thing is that Amadeus responses use multiple namespaces. This response uses just 2. Other responses, eg searches, use many more.
You might consider working with System.Xml.XmlDocument and System.Xml.XPath.XPathNavigator which are really easy to work with.
I wrote a simple example for you (supporting UTF-8 encoding):
System.Xml.XmlDocument someXmlFile = new System.Xml.XmlDocument();
string xmlPath= #"YOUR_XML_FULL_PATH";
string innerNodeToExtract= #"/Session/SecurityToken";
try
{
// Loading xml file with utf-8 encoding:
string xmlFileStr= Systm.IO.File.ReadAllText(xmlPath, System.Text.Encoding.UTF8);
// Creating the XPathNavigator:
System.Xml.XPath.XPathNavigator xmlNavigator= someXmlFile.CreateNavigator();
// Getting the inner value as string:
string value = xmlNavigator.SelectSingleNode(innerNodeToExtract).Value;
// some code...
}
catch(Exception)
{
// Handle failures
}
Please notice that you can also:
Extract inner values using "#" key.
Move to the child using xmlNavigator.MoveToNext().
And many other things that you can read here.

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();

XElement descendant not working \ XElement.Parse() [duplicate]

I'm a completly New to Linq2XML as I code to much lines to perform simple things, and in a simple project I wanted to give it a try...
I'm with this for 2 hours and nothing I do get's it right :(
I'm really, really thinking to go back to XmlNode-code-alike
The Task:
I send a SOAP Action to an ASMX service and I get the response as XML
I Parse the XML into a XDocument object
I try to get a list of nodes ... err! Problem!
as you can see from this screenshot
alt text http://www.balexandre.com/temp/2010-02-26_0038.png
my XDocument has a Node called TransactionInformationType witch is a Sequence, and I simple want to get all and retrieve the only 2 variables that I need (you can see the code commented) just below select c;
in the Watch window you can see that
doc.Descendants("TransactionInformationType")
returns nothing at all, and seeing by the content of the XDocument in the Text Visualizer, it does exist!
Anyone care to explain and help me passing this HUGE wall?
Thank you!
Added
XDocument content
Answer
the Response XML has
<gettransactionlistResponse xmlns="https://ssl.ditonlinebetalingssystem.dk/remote/payment">
and I must use this as Namespace!
turns out that, to retrieve values, I do need to use the XNamespace as well, so the final code looks like this:
// Parse XML
XDocument doc = XDocument.Parse(strResponse);
XNamespace ns = "https://ssl.ditonlinebetalingssystem.dk/remote/payment";
var trans = from item in doc.Descendants(ns + "TransactionInformationType")
select new TransactionInformationType
{
capturedamount = Convert.ToInt32(item.Element(ns + "capturedamount").Value),
orderid = item.Element(ns + "cardtypeid").Value
};
Thank you all for the help!
var result = doc.Descendants("TransactionInformationType");
selects all descendants in the XDocument that have element name "TransactionInformationType" and are in the empty namespace.
From you screenshot it seems the element you're trying to select is in the namespace "https://ssl.ditonlinebetalingssystem.dk/remote/payment" though.
You need to specify that explicitly:
XNamespace ns = "https://ssl.ditonlinebetalingssystem.dk/remote/payment";
↑↑ ↑
var result = doc.Descendants(ns + "TransactionInformationType");
This should solve you isssue (replace the namespace with the right URL):
XNamespace ns = "https://ssl.ditonline...";
doc.Descendants(ns + "TransactionInformationType");

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)

Categories