Trying to get the value for the "title" node from this XML -> http://feeds.feedburner.com/dotnetshoutout-published
I am using this code:
var d = XDocument.Load("http://feeds.feedburner.com/dotnetshoutout-published");
var node = d.Root.Descendants().Where(x => x.Name == "title").FirstOrDefault();
Always returns null. Making me crazy, any assistance is appreciated.
I guess you have an Xml Namespace on your elements.If so, your element name won't be just title, it will be namespace + title.Instead you should check the LocalName :
var node = d.Root.Descendants().Where(x => x.LocalName == "title").FirstOrDefault();
Or, you can look at the namespace of your elements and create an XNamespace and use it to fetch elements:
XNamespace ns = "yournamespace";
var node = d.Root.Descendants(ns + "title").FirstOrDefault();
You can read the documentation to find more info about how to deal with xml namespaces.
Related
I have a xml file:
<?xml version="1.0" encoding="utf-8"?>
<ApplicationConfiguration xmlns="http://test.org/SDK/Configuration.xsd">
<ApplicationName>
<ApplicationUri>123</ApplicationUri>
<ApplicationUri>456</ApplicationUri>
</ApplicationName>
</ApplicationConfiguration>
What I want is set the value of ApplicationUri from 456 to 789 in C# code.
I wrote this code:
string docaddress = "testfile.xml";
XDocument doc = XDocument.Load(docaddress);
doc.Element("ApplicationConfiguration")
.Elements("ApplicationName").FirstOrDefault()
.SetElementValue("ApplicationUri", "789");
doc.Save(docaddress);
The problems are:
There is no error while running. I think the element ApplicationConfiguration is not correct. But when I delete the line xmlns=... from the xml file, it runs normally
The value 789 is replaced with 123, but not 456 as I want (same element name)
Can you tell me how to fix those problems?
Hello and welcome to Stack Overflow!
The xmlns attribute on the ApplicationConfiguration element makes that the root.
So you get the root first. Then you replace the values of each descendant, selecting locally by name with the element name you want. something like this:
string docaddress = "C:\\temp\\testfile.xml";
XDocument doc = XDocument.Load(docaddress);
var root = doc.Root;
var descendants = root.Descendants();
var these = root.Descendants().Where(p => p.Name.LocalName == "ApplicationUri");
foreach (var elem in these)
{
elem.Value = "789";
}
doc.Save(docaddress);
I added namespace to your code :
string docaddress = "testfile.xml";
XDocument doc = XDocument.Load(docaddress);
XNamespace ns = doc.Root.GetDefaultNamespace();
doc.Element(ns + "ApplicationConfiguration")
.Elements(ns + "ApplicationName").FirstOrDefault()
.SetElementValue(ns + "ApplicationUri", "789");
doc.Save(docaddress);
IMHO, here is an easiest method.
It is taking care of the XML default namespace.
No loops. Set based approach.
c#
void Main()
{
const string fileName = #"e:\temp\hala.xml";
const string searchFor = "456";
const string replaceWith = "789";
XDocument doc = XDocument.Load(fileName);
XNamespace ns = doc.Root.GetDefaultNamespace();
// step #1: find element based on the search value
XElement xmlFragment = doc.Descendants(ns + "ApplicationUri")
.Where(d => d.Value.Equals(searchFor)).FirstOrDefault();
// step #2: if found, set its value
if(xmlFragment != null)
xmlFragment.SetValue(replaceWith);
doc.Save(fileName);
}
I have the below XML and I've been trying to extract the FirstName, LastName and OtherName for a while now I'm running into all sort of problems.
<OmdCds xmlns="cds"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cdsd="cds_dt"
xsi:schemaLocation="cds ontariomd_cds.xsd">
<PatientRecord>
<Demographics>
<Names>
<cdsd:LegalName namePurpose="L">
<cdsd:FirstName>
<cdsd:Part>SARAH</cdsd:Part>
<cdsd:PartType>GIV</cdsd:PartType>
<cdsd:PartQualifier>BR</cdsd:PartQualifier>
</cdsd:FirstName>
<cdsd:LastName>
<cdsd:Part>GOMEZ</cdsd:Part>
<cdsd:PartType>FAMC</cdsd:PartType>
<cdsd:PartQualifier>BR</cdsd:PartQualifier>
</cdsd:LastName>
<cdsd:OtherName>
<cdsd:Part>GABRIELA</cdsd:Part>
<cdsd:PartType>GIV</cdsd:PartType>
<cdsd:PartQualifier>BR</PartQualifier>
I currently trying to extract with the below c# code but still can't extract the above data. I'm getting a nullreferenceexception.
XmlDocument doc = new XmlDocument();
doc.Load(folder + "\\" + o.ToString());
XmlNamespaceManager namespaceManager = new XmlNamespaceManager(doc.NameTable);
namespaceManager.AddNamespace("cdsd", "http://www.w3.org/2001/XMLSchema-instance");
XmlNode firstName = doc.DocumentElement.SelectSingleNode("/PatientRecord/Demographics/Names/cdsd:LegalName/cdsd:FirstName/cdsd:Part", namespaceManager);
string fName = firstName.InnerText;
MessageBox.Show(fName);
I can see in the local watch item under doc.DocumentElement, all the InnerXML and InnerText. The InnerXML look something like this...
<PatientRecord xmlns=\"cds\"><Demographics><Names><cdsd:LegalName namePurpose=\"L\" xmlns:cdsd=\"cds_dt\"><cdsd:FirstName><cdsd:Part>SARAH</cdsd:Part><cdsd:PartType>GIV</cdsd:PartType><cdsd:PartQualifier>BR</cdsd:PartQualifier></cdsd:FirstName>
You have 3 namespace definitions in the document:
cds - as a default namespace
http://www.w3.org/2001/XMLSchema-instance- with the xsi prefix
cds_dt - with the cdsd prefix
I am wondering that you don't get an error message, because cds and cds_dt are no URIs and namspaces need to be URIs.
If you try to understand an element name you need to replaces the prefix with the actual namespace.
<PatientRecord> reads as {cds}:PatientRecord
<cdsd:LegalName> reads as {cds_dt}:LegalName
Now in XPath 1.0 the same happens with registered namespaces. But XPath does not have a default namespace. So elements without one are not expanded with a default namespace.
You need to register namespace prefixes on the namespace manager. The prefix does not need to be the same as in the document.
namespaceManager.AddNamespace("cdsd", "cds_dt");
namespaceManager.AddNamespace("cds", "cds");
Now you can use the registered namespaces in XPath:
doc.DocumentElement.SelectSingleNode(
"cds:PatientRecord/cds:Demographics/cds:Names/cdsd:LegalName/cdsd:FirstName/cdsd:Part",
namespaceManager
);
If the first character of an XPath expression is a slash the expression is relative to the document, otherwise to the current context node. You call SelectSingleNode() on the doc.DocumentElement - the OmdCds element node. PatientRecord is a child node, so you can start with it or use . for the current context node.
PatientRecord, Demographics and Names are in the cds namespace. This is because of the default namespace declaration on the OmdCds element (xmlns="cds"). The others are in the cdsd namespace, not xsi. You'll have to add them and use them in the XPATH:
namespaceManager.AddNamespace("cdsd", "cdsd");
namespaceManager.AddNamespace("cds", "cds");
XmlNode firstName = doc.DocumentElement.SelectSingleNode(
"/cds:PatientRecord/cds:Demographics/cds:Names/cdsd:LegalName/cdsd:FirstName/cdsd:Part",
namespaceManager);
BTW, you're getting a NullReferenceException because you're making the false assumption that your query will always return a node. You are now seeing what happens when it does not return a node. Always check for null whenever it's possible that a query returns no value.
Instead XmlDocument class you can use Linq to XML, is easy. You need using the System.Xml.Linq namspace, for example:
XDocument xdoc = XDocument.Load("path");
IEnumerable<XElement> nodes = (from p in xdoc.Descendants()
where p.Name.LocalName == "FirstName"
select p).Elements();
foreach (XElement nodeFirstName in nodes)
{
foreach (XElement parts in nodeFirstName.Elements())
{
string strExtracted = parts.Name.LocalName + " " + parts.Value;
}
}
The LocalName property is used beacuse elements have a prefix "cdsd"
Load function is already defined in xmlData class
public class XmlData
{
public void Load(XElement xDoc)
{
var id = xDoc.XPathSelectElements("//ID");
var listIds = xDoc.XPathSelectElements("/Lists//List/ListIDS/ListIDS");
}
}
I'm just calling the Load function from my end.
XmlData aXmlData = new XmlData();
string input, stringXML = "";
TextReader aTextReader = new StreamReader("D:\\test.xml");
while ((input = aTextReader.ReadLine()) != null)
{
stringXML += input;
}
XElement Content = XElement.Parse(stringXML);
aXmlData.Load(Content);
in load function,im getting both id and and listIds as null.
My test.xml contains
<SEARCH>
<ID>11242</ID>
<Lists>
<List CURRENT="true" AGGREGATEDCHANGED="false">
<ListIDS>
<ListID>100567</ListID>
<ListID>100564</ListID>
<ListID>100025</ListID>
<ListID>2</ListID>
<ListID>1</ListID>
</ListIDS>
</List>
</Lists>
</SEARCH>
EDIT: Your sample XML doesn't have an id element in the namespace with the nss alias. It would be <nss:id> in that case, or there'd be a default namespace set up. I've assumed for this answer that in reality the element you're looking for is in the namespace.
Your query is trying to find an element called id at the root level. To find all id elements, you need:
var tempId = xDoc.XPathSelectElements("//nss:id", ns);
... although personally I'd use:
XDocument doc = XDocument.Parse(...);
XNamespace nss = "http://schemas.microsoft.com/SQLServer/reporting/reportdesigner";
// Or use FirstOrDefault(), or whatever...
XElement idElement = doc.Descendants(nss + "id").Single();
(I prefer using the query methods on LINQ to XML types instead of XPath... I find it easier to avoid silly syntax errors etc.)
Your sample code is also unclear as you're using xDoc which hasn't been declared... it helps to write complete examples, ideally including everything required to compile and run as a console app.
I am looking at the question 3 hours after it was submitted and 41 minutes after it was (last) edited.
There are no namespaces defined in the provided XML document.
var listIds = xDoc.XPathSelectElements("/Lists//List/ListIDS/ListIDS");
This XPath expression obviously doesn't select any node from the provided XML document, because the XML document doesn't have a top element named Lists (the name of the actual top element is SEARCH)
var id = xDoc.XPathSelectElements("//ID");
in load function,im getting both id and and listIds as null.
This statement is false, because //ID selects the only element named ID in the provided XML document, thus the value of the C# variable id is non-null. Probably you didn't test thoroughly after editing the XML document.
Most probably the original ID element belonged to some namespace. But now it is in "no namespace" and the XPath expression above does select it.
string xmldocument = "<response xmlns:nss=\"http://schemas.microsoft.com/SQLServer/reporting/reportdesigner\"><action>test</action><id>1</id></response>";
XElement Content = XElement.Parse(xmldocument);
XPathNavigator navigator = Content.CreateNavigator();
XmlNamespaceManager ns = new XmlNamespaceManager(navigator.NameTable);
ns.AddNamespace("nss", "http://schemas.microsoft.com/SQLServer/reporting/reportdesigner");
var tempId = navigator.SelectSingleNode("/id");
The reason for the null value or system returned value is due to the following
var id = xDoc.XPathSelectElements("//ID");
XpathSElectElements is System.xml.linq.XElment which is linq queried date. It cannot be directly outputed as such.
To Get individual first match element
use XPathSelectElement("//ID");
You can check the number of occurrences using XPathSelectElements as
var count=xDoc.XPathSelectElements("//ID").count();
you can also query the linq statement as order by using specific conditions
Inorder to get node value from a list u can use this
foreach (XmlNode xNode in xDoc.SelectNodes("//ListIDS/ListID"))
{
Console.WriteLine(xNode.InnerText);
}
For Second list you havnt got the value since, the XPath for list items is not correct
I have an XML document. I want to retrieve a specific descendant node of the root node. The root node does not have a namespace, however, the children nodes do, although they are all the same. What is the best way to retrieve this element as an element?
The namespace of the root node doesn't matter.
You can just write
XNamespace ns = "http://...";
var elem = doc.Element(ns + "TagName");
If you don't know the namespace of the children you could match them by LocalName, which refers to the local (unqualified) part of the name.
string name = "purchase";
var query = xml.Descendants()
.Where(e => e.Name.LocalName == name);
This returns an IEnumerable<XElement>. From there you can loop over it or use SingleOrDefault if you expect only one to exist.
<root xmlns:h="http://www.w3.org/TR/html4/"
xmlns:f="http://www.w3schools.com/furniture">
<h:table>
<h:tr>
<h:td>Apples</h:td>
<h:td>Bananas</h:td>
</h:tr>
</h:table>
<f:table>
<f:name>African Coffee Table</f:name>
<f:width>80</f:width>
<f:length>120</f:length>
</f:table>
</root>
I am trying to practice LinqToXml but i can't figure out what i wanted.Simply how can i query table elements which has h or f namespace ?
This was what i tried .Also i tried different ones but didn't work.
var query = from item in XDocument.Parse(xml).Elements(ns + "table")
select item;
This won't work because you're missing the root element from your query. This would work:
XNamespace ns = "http://www.w3schools.com/furniture";
var query = XDocument.Parse(xml).Element("root").Elements(ns + "table");
Now if the problem is that you want to find all "table" elements regardless of the namespace, you'd need something like this:
var query = XDocument.Parse(xml)
.Element("root")
.Elements()
.Where(element => element.Name.LocalName == "table");
(EDIT: As noted, you could use XDocument.Root to get to the root element if you want to. The important point is that trying to get to the table element directly from the document node itself won't work.)
Namespace prefixes are not guaranteed to be a particular letter or string. The best approach would be to search by the qualified namespace.
This would get all direct child nodes of XElement xml where the namespace is uri:namespace...
var selectedByNamespace = from element in xml.Elements()
where element.Name.NamespaceName == "uri:namespace"
select element;
Another option would be to select the elements based on the fully qualified name.
var ns = "{uri:namespace}";
var selectedElements = xml.Elements(ns + "table");