XML namespace prefix error - c#

I am using c# to read (attempting to) an RSS feed, but I am getting an error "Namespace prefix 'cb' is not defined" , I am pretty new to XML and C# and was hoping for some help, I read a bit on creating the Namespace but I am not 100% sure I am grasping it.
Any help would be greatly appreciated.
The C# code is:
/*
Add rows by calling the AddRow method on the member variable named "<Output Name>Buffer".
For example, call MyOutputBuffer.AddRow() if your output was named "MyOutput".
*/
// Create an XmlNamespaceManager to resolve the default namespace.
XmlDocument xm = new XmlDocument();
xm.Load(Variables.USFeed.ToString());
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xm.NameTable);
nsmgr.AddNamespace("rdf", "http://purl.org/rss/1.0/");
XmlNodeList xnode = xm.GetElementsByTagName("item");
foreach (XmlNode xmn in xnode)
{
XmlElement currencyElement = (XmlElement)xmn;
if (currencyElement.HasAttribute("rdf:about"))
{
Output0Buffer.AddRow();
Output0Buffer.observationPeriod = currencyElement.SelectSingleNode("cb:statistics/cb:exchangeRate/cb:observationPeriod", nsmgr).InnerText;
Output0Buffer.targetCurrency = currencyElement.SelectSingleNode("cb:statistics/cb:exchangeRate/cb:targetCurrency", nsmgr).InnerText;
Output0Buffer.baseCurrency = currencyElement.SelectSingleNode("cb:statistics/cb:exchangeRate/cb:baseCurrency", nsmgr).InnerText;
Output0Buffer.exchangeRate = double.Parse(currencyElement.SelectSingleNode("cb:statistics/cb:exchangeRate/cb:value", nsmgr).InnerText);
}
}
and the summarized version of the rss is:
<?xml version="1.0" encoding="ISO-8859-1"?>
<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://purl.org/rss/1.0/"
xmlns:cb="http://www.cbwiki.net/wiki/index.php/Specification_1.1"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:dcterms="http://purl.org/dc/terms/"
xmlns:xsi="http://www.w3c.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.w3c.org/1999/02/22-rdf-syntax-ns#rdf.xsd">
<channel rdf:about="http://www.bankofcanada.ca/stats/assets/rates_rss/noon/en_ALL.xml">
<title xml:lang="en">Bank of Canada: Noon Foreign Exchange Rates</title>
<link>http://www.bankofcanada.ca/rates/exchange/noon-rates-5-day/</link>
<description>Current day's noon foreign exchange rates from the Bank of Canada. Published at about 12:15 ET.</description>
<items>
<rdf:Seq>
<rdf:li rdf:resource="http://www.bankofcanada.ca/stats/assets/rates_rss/noon/en_USD.xml" />
<rdf:li rdf:resource="http://www.bankofcanada.ca/stats/assets/rates_rss/noon/en_VEF.xml" />
<rdf:li rdf:resource="http://www.bankofcanada.ca/stats/assets/rates_rss/noon/en_VND.xml" />
</rdf:Seq>
</items>
</channel>
<item rdf:about="http://www.bankofcanada.ca/stats/assets/rates_rss/noon/en_USD.xml">
<title xml:lang="en">CA: 0.9382 USD = 1 CAD 2014-01-06 Bank of Canada noon rate</title>
<cb:statistics>
<cb:country>CA</cb:country>
<cb:exchangeRate>
<cb:value decimals="4">0.9382</cb:value>
<cb:baseCurrency>CAD</cb:baseCurrency>
<cb:targetCurrency>USD</cb:targetCurrency>
<cb:rateType>Bank of Canada noon rate</cb:rateType>
<cb:observationPeriod frequency="daily">2014-01-06T12:15:00-05:00</cb:observationPeriod>
</cb:exchangeRate>
</cb:statistics>
</item>
<item rdf:about="http://www.bankofcanada.ca/stats/assets/rates_rss/noon/en_ARS.xml">
<title xml:lang="en">CA: 6.1843 ARS = 1 CAD 2014-01-06 Bank of Canada noon rate</title>
<cb:statistics>
<cb:country>CA</cb:country>
<cb:exchangeRate>
<cb:value decimals="4">6.1843</cb:value>
<cb:baseCurrency>CAD</cb:baseCurrency>
<cb:targetCurrency>ARS</cb:targetCurrency>
<cb:rateType>Bank of Canada noon rate</cb:rateType>
<cb:observationPeriod frequency="daily">2014-01-06T12:15:00-05:00</cb:observationPeriod>
</cb:exchangeRate>
</cb:statistics>
</item>

You need to add "cb" to your XmlNamespaceManager in order to use "cb" in SelectSingleNode.
nsmgr.AddNamespace(
"cb",
"http://www.cbwiki.net/wiki/index.php/Specification_1.1");

Related

Xpath for Decendent Child

I have a response XML in which I am trying to find the id of Entry tag, but any combination is yielding null always.
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<status feed="http://feeds.bbci.co.uk/news/rss.xml?edition=int" xmlns="http://superfeedr.com/xmpp-pubsub-ext">
<http code="200">Fetched (ring) 200 242 and parsed 2/42 entries</http>
<next_fetch>1970-01-18T20:24:54.289Z</next_fetch>
<entries_count_since_last_maintenance>35</entries_count_since_last_maintenance>
<velocity>65.3</velocity>
<popularity>3.713318235496007</popularity>
<generated_ids>true</generated_ids>
<title>BBC News - Home</title>
<period>242</period>
<last_fetch>1970-01-18T20:24:54.045Z</last_fetch>
<last_parse>1970-01-18T20:24:54.045Z</last_parse>
<last_maintenance_at>1970-01-18T20:24:07.350Z</last_maintenance_at>
</status>
<link title="BBC News - Home" rel="alternate" href="https://www.bbc.co.uk/news/" type="text/html"/>
<link title="BBC News - Home" rel="image" href="https://news.bbcimg.co.uk/nol/shared/img/bbc_news_120x60.gif" type="image/gif"/>
<title>BBC News - Home</title>
<updated>2018-11-15T14:59:15.000Z</updated>
<id>bbc-news-home-2018-11-15-14</id>
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:geo="http://www.georss.org/georss" xmlns:as="http://activitystrea.ms/spec/1.0/" xmlns:sf="http://superfeedr.com/xmpp-pubsub-ext" xml:lang="en">
<id>https://www.bbc.co.uk/news/world-us-canada-46225486</id>
<published>2018-11-15T14:44:37.000Z</published>
<updated>2018-11-15T14:44:37.000Z</updated>
<title>Trump attacks Mueller's Russia inquiry as 'absolutely nuts'</title>
<summary type="text">The US president says the Russia inquiry is a "total mess" and calls investigators "a disgrace".</summary>
<link title="Trump attacks Mueller's Russia inquiry as 'absolutely nuts'" rel="alternate" href="https://www.bbc.co.uk/news/world-us-canada-46225486" type="text/html" xml:lang="en"/>
<link title="Trump attacks Mueller's Russia inquiry as 'absolutely nuts'" rel="thumbnail" href="http://c.files.bbci.co.uk/E64B/production/_104355985_gettyimages-1060191940.jpg" type="image/jpeg" xml:lang="en"/>
</entry>
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:geo="http://www.georss.org/georss" xmlns:as="http://activitystrea.ms/spec/1.0/" xmlns:sf="http://superfeedr.com/xmpp-pubsub-ext" xml:lang="en">
<id>https://www.bbc.co.uk/news/world-africa-46221238</id>
<published>2018-11-15T14:35:47.000Z</published>
<updated>2018-11-15T14:35:47.000Z</updated>
<title>Ethiopia arrests former deputy spy chief Yared Zerihun</title>
<summary type="text">Prime Minister Abiy Ahmed promised to combat corruption and rights abuses when he took office.</summary>
<link title="Ethiopia arrests former deputy spy chief Yared Zerihun" rel="alternate" href="https://www.bbc.co.uk/news/world-africa-46221238" type="text/html" xml:lang="en"/>
<link title="Ethiopia arrests former deputy spy chief Yared Zerihun" rel="thumbnail" href="http://c.files.bbci.co.uk/52E9/production/_104352212_872d41ed-8ac9-4b7b-abfc-b4d898a71670.jpg" type="image/jpeg" xml:lang="en"/>
</entry>
</feed>
to fetch the id these are the combination I tried
"/feed/entry/id/text()"
"entry/id/text()"
`doc.GetElementsByTagName("entry").SelectNodes("id/text()")I
i can go to id by iteration with childNodes but what will be XPath for that.
but if I try "/*" on the whole document it gives me one node count. why ?
The xml elements in the xml are declared within the http://www.w3.org/2005/Atom xml namespace.
This namespace has to be considered in the XPATHstatement.
You have to register this namespace with an XmlNamespaceManager and apply the chosen prefix (here:x) in the XPATHstatement as: //x:feed/x:entry/x:id.
XmlDocument doc = new XmlDocument();
String pathToYourXmlFile = #"c:\folder\file.xml";
doc.Load(pathToYourXmlFile);
XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("x", "http://www.w3.org/2005/Atom");
XmlNodeList ids = doc.SelectNodes("//x:feed/x:entry/x:id", nsmgr);
foreach (XmlNode id in ids)
{
Console.WriteLine(id.InnerText);
}
You xml contains namespace xmlns="http://www.w3.org/2005/Atom" at root level node <feed>
And you are using /feed/entry/id/text() these kind of XPath but these path are not suitable for this xml and that's why you can't get any desired value.
You need to use below XPath to get id's of all <entry> node.
var ids = doc.SelectNodes("//*[name()='feed']/*[name()='entry']/*[name()='id']/text()");
Here i created a sample console app for demonstration purpose.
class program
{
public static void Main()
{
XmlDocument doc = new XmlDocument();
doc.Load(#"Path to your xml file");
var ids = doc.SelectNodes("//*[name()='feed']/*[name()='entry']/*[name()='id']/text()");
foreach (XmlNode id in ids)
{
Console.WriteLine(id.Value);
}
Console.ReadLine();
}
}
Output:

How to get enclosure url with XElement C# Console

I read multiple feed from many sources with C# Console, and i have this code where i load XML From sources:
XmlDocument doc = new XmlDocument();
doc.Load(sourceURLX);
XElement xdoc = XElement.Load(sourceURLX);
How to get enclosure url and show as variable?
If I understand your question correctly (I'm making a big assumption here) - you want to select an attribute from the root (or 'enclosing') tag, named 'url'?
You can make use of XPath queries here. Consider the following XML:
<?xml version="1.0" encoding="utf-8"?>
<root url='google.com'>
<inner />
</root>
You could use the following code to retrieve 'google.com':
String query = "/root[1]/#url";
XmlDocument doc = new XmlDocument();
doc.Load(sourceURLX);
String value = doc.SelectSingleNode(query).InnerText;
Further information about XPath syntax can be found here.
Edit: As you stated in your comment, you are working with the following XML:
<item>
<description>
</description>
<enclosure url="blablabla.com/img.jpg" />
</item>
Therefore, you can retrieve the url using the following XPath query:
/item[1]/enclosure[1]/#url
With xml like below
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
<title>title</title>
<link>https://www.link.com</link>
<description>description</description>
<item>
<title>RSS</title>
<link>https://www.link.com/xml/xml_rss.asp</link>
<description>description</description>
<enclosure url="https://www.link.com/media/test.wmv"
length="10000"
type="video/wmv"/>
</item>
</channel>
</rss>
You will get url by reading attribute
var document = XDocument.Load(sourceURLX);
var url = document.Root
.Element("channel")
.Element("item")
.Element("enclosure")
.Attribute("url")
.Value;
To get multiple urls
var urls = document.Descendants("item")
.Select(item => item.Element("enclosure").Attribute("url").Value)
.ToList();
Using foreach loop
foreach (var item in document.Descendants("item"))
{
var title = item.Element("title").Value;
var link = item.Element("link").Value;
var description = item.Element("description").Value;
var url = item.Element("enclosure").Attribute("url").Value;
// save values to database
}

SelectNodes does not return the child values

I am new to XPath. I read the entire W3Schools tutorial. I would like to get all the <schedule> nodes of my document. I can get all the child elements of my document with child::* but as soon as I add <schedule> like the following, I get zero results:
XmlDocument dom = new XmlDocument();
dom.Load(textBoxFilePath.Text);
XmlNodeList jobElements = dom.DocumentElement.SelectNodes("child::schedule");
This is my xml:
<?xml version="1.0" encoding="UTF-8"?>
<!-- This file contains job definitions in schema version 2.0 format -->
<job-scheduling-data version="2.0" xmlns="http://quartznet.sourceforge.net/JobSchedulingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<processing-directives>
<overwrite-existing-data>true</overwrite-existing-data>
</processing-directives>
<schedule>
<job>
<name>receiverjob</name>
<group>receivergroup</group>
<job-type>Quartz.Server.ArgumentReceiverJob, Quartz.Server</job-type>
<job-data-map>
<entry>
<key>receivedargument</key>
<value>hamburger</value>
</entry>
</job-data-map>
</job>
<trigger>
<simple>
<name>argumentreceiverJobTrigger</name>
<group>argumentreceiverGroup</group>
<description>Simple trigger to simply fire sample job</description>
<job-name>receiverjob</job-name>
<job-group>receivergroup</job-group>
<misfire-instruction>SmartPolicy</misfire-instruction>
<repeat-count>-1</repeat-count>
<repeat-interval>10000</repeat-interval>
</simple>
</trigger>
<job>
<name>batchjob</name>
<group>batchGroup</group>
<job-type>Quartz.Server.BatchJob, Quartz.Server</job-type>
<durable>true</durable>
<recover>false</recover>
</job>
<trigger>
<cron>
<name>Trigger2</name>
<group>DEFAULT</group>
<job-name>batchjob</job-name>
<job-group>batchGroup</job-group>
<cron-expression>0/15 * * * * ?</cron-expression>
</cron>
</trigger>
</schedule>
</job-scheduling-data>
What I would ultimately like to achieve is to get all the <name>s of the <job>s that match a string.
That's because your XML has default namespace :
xmlns="http://quartznet.sourceforge.net/JobSchedulingData"
Register a prefix that points to default namespace, then use that prefix along with the element's local name to reference an element in namespace :
XmlDocument dom = new XmlDocument();
dom.Load(textBoxFilePath.Text);
XmlNamespaceManager nsManager = new XmlNamespaceManager(dom.NameTable);
nsManager.AddNamespace("d", dom.DocumentElement.NamespaceURI);
XmlNodeList jobElements = dom.DocumentElement.SelectNodes("child::d:schedule", nsManager);
.NET fiddle demo
You could use the following code to find all schedule elements.
XmlDocument dom = new XmlDocument();
dom.Load(textBoxFilePath.Text);
XmlNodeList jobElements = dom.GetElementsByTagName("schedule");

Adding new RSS elements to existing RSS, without modifying existing namespace prefixes

I have an input RSS Feed with some elements already added with namespace prefix (for itunes). Without removing attribute and adding in C# again, an element, say <itunes:subtitle> is added as namespace and the element is <subtitle>
Desired output:
<rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd/" version="2.0">
<channel>
<title>channelTitle</title>
<itunes:subtitle>subtitle_description</itunes:subtitle>
<item>
<title>item1</title>
<itunes:subtitle>A short description</itunes:subtitle>
</item>
</channel>
</rss>
Input XML:
<rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd/" version="2.0">
<channel>
<item>
<title>item1</title>
<itunes:subtitle>A short description</itunes:subtitle>
</item>
</channel>
</rss>
How can I add another element in C#, but also maintain the existing namespace:element ? I'm having to explicitly add the namespace again in the code (and namespace should also be present in input XML, otherwise it's processing invalid XML:
See code:
XNamespace itunes = "http://www.itunes.com/dtds/podcast-1.0.dtd/";
string rssFeed = "<rss xmlns:itunes=\"http://www.itunes.com/dtds/podcast-1.0.dtd\" version=\"2.0\"><channel><item><title>item1</title><itunes:subtitle>A short description</itunes:subtitle></item></channel></rss>";
XDocument XMLDoc = XDocument.Parse(rssFeed);
XMLDoc.Root.RemoveAttributes();
XMLDoc.Root.Add(new XAttribute(XNamespace.Xmlns + "itunes", itunes.NamespaceName));
//Without adding the namespace attribute explicitly, the xmlns attribute is added instead to <subtitle> instead of <itunes:subtitle> :
XMLDoc.Element("rss").Element("channel").AddFirst(
new XElement("title", "channelTitle"),
new XElement(itunes + "subtitle", "subtitle_description")
);
gets added correctly.
However, is now changed in the input XML, output:
<rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd/">
<channel>
<title>channelTitle</title>
<itunes:subtitle>subtitle_description</itunes:subtitle>
<item>
<title>item1</title>
<subtitle xmlns="http://www.itunes.com/dtds/podcast-1.0.dtd">A short description</subtitle>
</item>
</channel>
</rss>
Approach2:
XNamespace itunes = "http://www.itunes.com/dtds/podcast-1.0.dtd/";
string rssFeed = "<rss xmlns:itunes=\"http://www.itunes.com/dtds/podcast-1.0.dtd\" version=\"2.0\"><channel><item><title>item1</title><itunes:subtitle>A short description</itunes:subtitle></item></channel></rss>";
XDocument XMLDoc = XDocument.Parse(rssFeed);
XMLDoc.Element("rss").Element("channel").AddFirst(
new XElement("title", "channelTitle"),
new XElement(itunes + "subtitle", "subtitle_description")
);
Console.WriteLine(XMLDoc);
Output:
<rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" version="2.0">
<channel>
<title>channelTitle</title>
<subtitle xmlns="http://www.itunes.com/dtds/podcast-1.0.dtd/">subtitle_description</subtitle>
<item>
<title>item1</title>
<itunes:subtitle>A short description</itunes:subtitle>
</item>
</channel>
</rss>
As much as my question is really long, I'm hoping to get few lines of code as an answer, sure there must be something simple I'm missing :)
I can't reproduce the problem you say you face, the following straightforward sample
string rssFeed = "<rss xmlns:itunes=\"http://www.itunes.com/dtds/podcast-1.0.dtd\" version=\"2.0\"><channel><item><title>item1</title><itunes:subtitle>A short description</itunes:subtitle></item></channel></rss>";
XDocument XMLDoc = XDocument.Parse(rssFeed);
XNamespace itunes = XMLDoc.Root.GetNamespaceOfPrefix("itunes");
XMLDoc.Element("rss").Element("channel").AddFirst(
new XElement("title", "channelTitle"),
new XElement(itunes + "subtitle", "subtitle_description")
);
XMLDoc.Save(Console.Out);
Console.WriteLine();
when run with .NET 3.5, outputs
<rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" version="2.0">
<channel>
<title>channelTitle</title>
<itunes:subtitle>subtitle_description</itunes:subtitle>
<item>
<title>item1</title>
<itunes:subtitle>A short description</itunes:subtitle>
</item>
</channel>
</rss>
Looking closer at your code, the only problem is see is that some samples use the URI http://www.itunes.com/dtds/podcast-1.0.dtd while some have http://www.itunes.com/dtds/podcast-1.0.dtd/ with a trailing slash "/". So that might be the reason why you run into problems.

How do I add a new node to the end of an existing XML file?

How do I add a new node to the end of an existing XML file?
I understand how, but how in the end?
For example, I have the following XML file and need to add a new node "entry" to the end of the file:
<?xml version="1.0" encoding="utf-8" ?>
- <entries>
- <entry type="debit">
<amount>100</amount>
<date>11.11.2010</date>
- <description>
- <![CDATA[ Описание записи]]>
</description>
<category>Продукты</category>
</entry>
- <entry type="credit">
<amount>50</amount>
<date>11.11.2010</date>
- <description>
- <![CDATA[ Описание записи]]>
</description>
<category>Продукты</category>
</entry>
- <entry type="debit">
<amount>100</amount>
<date>11.11.2010</date>
- <description>
- <![CDATA[ Описание записи]]>
</description>
<category>Продукты</category>
</entry>
</entries>
The simplest way is to load the XML into memory, append the child node, then write out the whole document again. For example:
XDocument doc = XDocument.Load("before.xml");
doc.Root.Add(new XElement("extra"));
doc.Save("after.xml");
If that isn't exactly what you were after, please clarify your question.
XmlDocument doc = new XmlDocument();
doc.LoadXml("before.xml");
//XmlNode root = doc.DocumentElement;
//Create a new node.
XmlElement elem = doc.CreateElement("entry");
elem.InnerText="";
//Add the node to the document.
//root.AppendChild(elem);
//Console.WriteLine("Display the modified XML...");
doc.LastChild.AppendChild(elem);
doc.Save("before.xml");'

Categories

Resources