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.
Related
Hi i was fiddeling around with xml files and i notised something i have a bit problem of solving.
I have a Xml that starts with a root node and then has another child node that can change name for example:
<root>
<Child1>
</root>
So given that "Child1" can be changed to "Child2" or "Child3" i made this linq be able to extract the name from whatever comes my way.
first:
XElement root = XElement.Parse(xml);
var childType = root.Descendants().First(x => x.Name == "Child1" || x.Name == "Child2"|| x.Name == "Child3").Name;
So when i have my xml without a namespace, as shown above, it workes fine, i manage to extract the name from the node tag.
But when i have a namespace into the root tag it throws an error:
<root xmlns="namespace">
<Child1>
</root>
That xml going through the same linq, throws:
Sequence contains no matching element
Your root element has a namespace defined (xmlns="namespace") thus all child elements are associated with the same namespace. I.e. Child1 element will be in the same namespace, and its name will contain both namespace prefix and local name ("Child1"). So you can either specify full name when searching for Child1 element:
var ns = root.GetDefaultNamespace();
var childType = root.Descendants()
.First(x => x.Name == ns +"Child1" || x.Name == ns + "Child2"|| x.Name == ns + "Child3")
.Name;
Or you can look for x.Name.LocalName (but I don't recommned this approach, though it's unlikely you will have Child1 elements from another namespace).
Note: your Child element does not have closing tag (probably it's a misprint)
Further reading: Xml Namespaces
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"
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.
I have an XML node with the following structure:
<package>package <name>com</name>.<name>sample</name>.<name>app1</name>;</package>
The name tag can appear a random number of times, and what I want is to get the values of all the child nodes between the first and last child in the list of elements, to get a value like this: com.sample.app1.
I guess I can apply some type of regular expression to the value of the package node which always starts with "package " and ends with ";". Or maybe some LINQ or lambda expression?
What would be the most efficient way of doing it since I'm processing a large XML file and this node is part of it?
Well in this case I'd just use:
var package = string.Join(".", element.Elements("name").Select(x => x.Value));
(Where element is the XElement representing <package>.)
Or if there's a different namespace:
XNamespace ns = "http://some.namespace/here";
var package = string.Join(".", element.Elements(ns + "name")
.Select(x => x.Value));
(Where element is the XElement representing <package> and ns + "name" combines a string with an XNamespace to give the right XName.)
<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");