Load an object from an XML with a namespace - c#

I have looked around and have not been able to implement a solutions for what I need. I apologize if this seems like a repeat questions.
I'm querying an IDOL service and need to load an object with from the xml response.
Give the sample xml:
<autnresponse xslns:autn="http://schemas.autonomy.com/aci/">
<action>QUERY</action>
<response>SUCCESS</response>
<responsedata>
<autn:numhits>2</autn:numhits>
<autn:totalhits>2</autn:totalhits>
<autn:totaldbdocs>2</autn:totaldbdocs>
<autn:totaldbsecs>2</autn:totaldbsecs>
<autn:hit>
<autn:reference>http://blah</autn:reference>
<autn:title>my title</autn:title>
</autn:hit>
<autn:hit>
<autn:reference>http://blah</autn:reference>
<autn:title>my title</autn:title>
</autn:hit>
</responsedata>
</autnresponse>
I'm trying to load a list of a custom object with linq to XML.
Here is what I have tried as code, and I'm always getting no results.
var namespaceManager = new XmlNamespaceManager(new NameTable());
namespaceManager.AddNamespace("autn", "http://schemas.autonomy.com/aci/");
... Load Xelemet.....
IEnumerable<XElement> urls = raw.Elements(IDOLModule.GetNamespace()+ "hit");
foreach (var single in urls)
{
var t = new url();
t.Title="";
t.Url="";
listURL.Add(t);
}
The urls variable is always coming back empty. I just need to target the autn:hits nodes and load their data to the object.
Cheers,

You could try something like that:
XElement doc = XElement.Parse(#"<autnresponse xmlns:autn=""http://schemas.autonomy.com/aci/"">
<action>QUERY</action>
<response>SUCCESS</response>
<responsedata>
<autn:numhits>2</autn:numhits>
<autn:totalhits>2</autn:totalhits>
<autn:totaldbdocs>2</autn:totaldbdocs>
<autn:totaldbsecs>2</autn:totaldbsecs>
<autn:hit>
<autn:reference>http://blah</autn:reference>
<autn:title>my title</autn:title>
</autn:hit>
<autn:hit>
<autn:reference>http://blah</autn:reference>
<autn:title>my title</autn:title>
</autn:hit>
</responsedata>
</autnresponse>");
XNamespace ns = ("http://schemas.autonomy.com/aci/");
//Console.WriteLine(doc.Descendants(ns + "numhits").First().Value);
IEnumerable<XElement> hits=doc.Descendants(ns + "hit");
foreach (XElement h in hits){
Console.WriteLine(String.Format("{0} - {1}",h.Name.ToString(),h.Elements().First().Value));
};

Related

Reading attribute from an XML file

I have an XML file which looks like this
<SendInvoiceResult xmlns="http://tempuri.org/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" IsSucceded="true">
<Value Id="123456" Number="1" InvoiceScenario="Scenario" />
</SendInvoiceResult>
I'm trying to read the attributes of this file but so far all my tries based on other questions on Stackoverflow either returned null or "Object reference not set to an instance of an object" error.
My latest try is this:
var testXml = new XmlDocument();
testXml.LoadXml(test);
var node = testXml.SelectSingleNode("/SendInvoiceResult/Value");
var id = node.Attributes["Id"].Value;
This approach returns "Object reference not set to an instance of an object" too. I'm wondering if something is wrong with the way XML is structures at this point.
I'm open to new methods and suggestions of course, all I need is to read the values of attributes in this and other similar XML files.
Use XDocument. It is much more modern and capable.
var doc = XElement.Load(test);
var id = doc.Root.Element("Value").Attribute("Id").Value;
You must define xml namespace.
var doc = new XmlDocument();
doc.Load("test.txt");
var manager = new XmlNamespaceManager(doc.NameTable);
manager.AddNamespace("ns", "http://tempuri.org/");
var node = doc.SelectSingleNode("/ns:SendInvoiceResult/ns:Value", manager);
var id = node.Attributes["Id"].Value;
Console.WriteLine(id);
Better use modern and more convenient linq to xml.
using System.Xml.Linq;
var doc = XDocument.Load("test.txt");
XNamespace ns = "http://tempuri.org/";
var id = doc.Root.Element(ns + "Value").Attribute("Id").Value;
Console.WriteLine(id);

How to Parse Nested XML Nodes in C#

I am very new to C#, but it seems as though this should be pretty straight forward. I am trying to parse an XML string returned from a web feed that looks like this:
<autnresponse xmlns:autn="http://schemas.autonomy.com/aci/">
<action>QUERY</action>
<response>SUCCESS</response>
<responsedata>
<autn:numhits>6</autn:numhits>
<autn:hit>
<autn:reference>http://something.what.com/index.php?title=DPM</autn:reference>
<autn:id>548166</autn:id>
<autn:section>0</autn:section>
<autn:weight>87.44</autn:weight>
<autn:links>Castlmania,POUCH</autn:links>
<autn:database>Postgres</autn:database>
<autn:title>A Pouch and Mail - Castlmania</autn:title>
<autn:content>
<DOCUMENT>
<DRETITLE>Castlmania Pouch and Mail - Castlmania</DRETITLE>
<DRECONTENT>A paragraph of sorts that would contain content</DRECONTENT>
</DOCUMENT>
</autn:content>
</autn:hit>
<autn:hit>...</autn:hit>
<autn:hit>...</autn:hit>
<autn:hit>...</autn:hit>
<autn:hit>...</autn:hit>
</autnresponse>
with no luck.
I am using this code to start:
XmlDocument xmlString = new XmlDocument();
xmlString.LoadXml(xmlUrl);
XmlElement root = xmlString.DocumentElement;
XmlNode GeneralInformationNode =
root.SelectSingleNode("//autnresponse/responsedata/autn:hit");
foreach (XmlNode node in GeneralInformationNode)
{
Console.Write("reference: "+node["autn:reference"]+" Title:"+node["DRETITLE"]+"<br />);
}
And I would like to print the DRETITLE and autn:reference element of within each of the autn:hit elements. Is that even doable with my approach?
I have tried looking and several example on the good old web like this to no avail.
The error that comes back is:
System.Xml.XPath.XpathEception {NameSpace Manager or XsltContext
needed. ...}
Thanks in advance.
UPDATE:
In trying to use XmlNamespaceManager, one has to give it a url to the schema definition like so:
XmlNamespaceManager namespmng = new XmlNamespaceManager (xmlString.NameTable);
namespmng.AddNamespace("autn","http://someURL.com/XMLschema");
The problem seems to be that now the error is gone, but the data is not displaying. I should mention that I am working off of a machine that does not have internet connectivity. The other thing is the schema seems to be unavailable. I am guessing that XmlNamespaceManager would work once able to connect to the internet right?
Using System.Xml.Linq it could be something like this:
var doc = XElement.Load(xmlUrl);
var ns = doc.GetNamespaceOfPrefix("autn");
foreach (var hit in doc.Descendants(ns + "hit"))
{
var reference = hit.Element(ns + "reference").Value;
var dretitle = hit.Descendants("DRETITLE").Single().Value;
WriteLine($"ref: {reference} title: {dretitle}");
}
Firstly, the exception you're getting is because you haven't loaded the namespace using the XmlNamespaceManager for the xml you're parsing. Something like this:
XmlNamespaceManager namespaceManager = new XmlNamespaceManager(xmlString.NameTable);
if (root.Attributes["xmlns:autn"] != null)
{
uri = root.Attributes["xmlns:autn"].Value;
namespaceManager.AddNamespace("autn", uri);
}
Secondly, what you're trying to do is possible. I'd suggest using root.SelectNodes(<your xpath here>) which will return a collection of autn:hit nodes that you can loop through instead of SelectSingleNode which will return one node. Within that you can drill down to the content/DOCUMENT/DRETITLE and pull the text for the DRETITLE node using either XmlNode.Value if you select the text specifically or XmlNode.InnerText on the DRETITLE node.

Parsing xml response - Select specific node based on value of another node

I'm new to xml so I'm not sure if I worded the question correctly, but I will do my best to explain.
Basically, I'm trying to parse an xml response in C# such as the one below:
<Premium>
<TotalPremiumAmount>87</TotalPremiumAmount>
<StandardPremium>87</StandardPremium>
<OptionalPremium>0</OptionalPremium>
<StandardTax>0</StandardTax>
<OptionalTax>0</OptionalTax>
<ExtendedTax>0</ExtendedTax>
<ExtendedPremium>0</ExtendedPremium>
<PromotionalPremium>0</PromotionalPremium>
<FeesPremium>0</FeesPremium>
<FeesTax>0</FeesTax>
<StandardFeesPremium>0</StandardFeesPremium>
<OptionalFeesPremium>0</OptionalFeesPremium>
<Tax>0</Tax>
<StandardPremiumDistribution>
<Travelers>
<Traveler>
<TravelerPremium>42</TravelerPremium>
<TravelerTax>0</TravelerTax>
</Traveler>
<Traveler>
<TravelerPremium>45</TravelerPremium>
<TravelerTax>0</TravelerTax>
</Traveler>
</Travelers>
</StandardPremiumDistribution>
<PackagePremiumDistribution>
<Packages>
<Package>
<PackageID>20265</PackageID>
<PackageName />
<PackageTypeID>12</PackageTypeID>
<Premium>87</Premium>
<Fees>0</Fees>
<Tax>0</Tax>
<Travelers>
<Traveler>
<TravelerID>0</TravelerID>
<Premium>42</Premium>
<Tax>0</Tax>
</Traveler>
<Traveler>
<TravelerID>1</TravelerID>
<Premium>45</Premium>
<Tax>0</Tax>
</Traveler>
</Travelers>
</Package>
</Packages>
</PackagePremiumDistribution>
</Premium>
I would like to get the value of the (Traveler) Premium. In the case of only one traveler, I have been using an XMLDocument and the 'SelectSingleNode" function. For example I could do something like:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(xmlResponse);
var premium = xmlDoc.SelectSingleNode("//TravelerPremium").InnerText;
But this wouldn't work when multiple travelers are returned under one plan. For example, I need the premium when TravelerID = 0. How would I go about doing this?
Thanks.
Using XmlDocument:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(xmlResponse);
var premium = xmlDoc.SelectSingleNode("//Premium[../TravelerID = '0']")
You could also iterate through the nodes if multiple could match on this condition like so:
foreach(var premium in xmldoc.SelectNodes("//Premium[../TravelerID = '0']")
{
// do work on each premium node where TravelerID = 0
}
I'd encourage you to look into using LINQ to XML - it's generally easier to work with and will be more performant in most cases. You could even still use XPath expressions, but the following would work:
XDocument xdoc = XDocument.Load(xmlResponse);
var premium = (string)xdoc.Descendants("Traveler").Where(x => (string)x.Element("TravelerID") == "0").Element("Premium");
Assuming your xml looks like that, try something like this:
XmlDocument doc = new XmlDocument();
xmlDoc.Load(xmlResponse);
if (doc.HasChildNodes)
{
foreach (XmlNode node in doc.DocumentElement.ChildNodes)
if (node.Name == "StandardPremiumDistribution")
{
XmlNodeList xnList = node.SelectNodes("//Travelers");
double travelerPremium= xnList.Item(z).FirstChild.InnerText);
}}
Based on this, I think you're gonna do it.
Let's suppose you have a file called XMLFile1.xml with the XML you posted you can iterate through all your TravelerPremium with the following code:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("XMLFile1.xml");
XmlNodeList premiums = xmlDoc.SelectNodes("//TravelerPremium");
foreach(XmlNode node in premiums)
{
MessageBox.Show(node.FirstChild.InnerText);
}
You can also acces the other elements with similar code.

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

stuck on xpath query

I have an xmlnodelist which results in the below snippet
<updi:ProductName xmlns:updi="urn:rosettanet:specification:universal:ProductIdentification:xsd:schema:01.04">Packet Processing Card (PPC) 16GB</updi:ProductName>
<ulc:AlternativeIdentifier xmlns:ulc="urn:rosettanet:specification:universal:Locations:xsd:schema:01.04">
<ulc:Authority>PID</ulc:Authority>
<ulc:Identifier>ASR5K-PPC-K9=</ulc:Identifier>
</ulc:AlternativeIdentifier>
<ulc:AlternativeIdentifier xmlns:ulc="urn:rosettanet:specification:universal:Locations:xsd:schema:01.04">
<ulc:Authority>CPN</ulc:Authority>
<ulc:Identifier />
</ulc:AlternativeIdentifier>
How can I grab the two authority and identifier tags? I tried adding a root element but I have issues with the namespaces. The original namespace declaration is very large.
Well, I doubt there's anything wrong with the XPath queries you've tried - more likely it's the namespaces that are tripping you up. There are a few ways to compensate/deal with this, ranging from "strip out all the namespaces" to using the XmlNamespaceManager - here's an example of that:
void Main()
{
var doc = new XmlDocument();
var namespaceMgr = new XmlNamespaceManager(doc.NameTable);
namespaceMgr.AddNamespace("updi", "urn:rosettanet:specification:universal:ProductIdentification:xsd:schema:01.04");
namespaceMgr.AddNamespace("ulc", "urn:rosettanet:specification:universal:Locations:xsd:schema:01.04");
doc.LoadXml(xml);
var authorityTags = doc.SelectNodes("//ulc:Authority", namespaceMgr);
var identifierTags = doc.SelectNodes("//ulc:Identifier", namespaceMgr);
}
Assuming xml is:
string xml = #"
<ROOT>
<updi:ProductName xmlns:updi=""urn:rosettanet:specification:universal:ProductIdentification:xsd:schema:01.04"">
Packet Processing Card (PPC) 16GB
</updi:ProductName>
<ulc:AlternativeIdentifier xmlns:ulc=""urn:rosettanet:specification:universal:Locations:xsd:schema:01.04"">
<ulc:Authority>PID</ulc:Authority>
<ulc:Identifier>ASR5K-PPC-K9=</ulc:Identifier>
</ulc:AlternativeIdentifier>
<ulc:AlternativeIdentifier xmlns:ulc=""urn:rosettanet:specification:universal:Locations:xsd:schema:01.04"">
<ulc:Authority>CPN</ulc:Authority>
<ulc:Identifier />
</ulc:AlternativeIdentifier>
</ROOT>";
The below will return all four nodes from your sample. I did have to wrap it in a root node for my testing. The | operator allows for the union functionality.
//ulc:AlternativeIdentifier/ulc:Authority | //ulc:AlternativeIdentifier/ulc:Identifier
I tested this using Notepad++ with the XPatherizerNPP plugin, which I highly recommend.
Something like this will do the trick:
XmlDocument doc = new XmlDocument();
doc.Load("YourXmlFile");
foreach (XmlNode node in doc.SelectNodes("//*[local-name() = \"Authority\"]"))
{
Console.WriteLine("Authority: " + node.InnerText);
}
foreach (XmlNode node in doc.SelectNodes("//*[local-name() = \"Identifier\"]"))
{
Console.WriteLine("Identifier: " + node.InnerText);
}
Basically, SelectNodes("//*[local-name() = \"Identifier\"]") tells it to search for node in the xml with that name, regardless of namespace, etc.

Categories