How to retrieve elements in particular namespace? - c#

I use C#. Please consider following XML:
<item>
<title>lorem</title>
<description>ipsum</description>
<media:category>Sports</media:category>
<media:title>Combo</media:title>
<media:thumbnail url='http://dolor/0.jpg' height='100' width='200'/>
<media:thumbnail url='http://sit/0.jpg' height='300' width='400'/>
</item>
I would like to be able to retrieve a List of all elements that have namespace eg. media.
The XML is loaded into XElement. I am able to find elements using Linq by name, eg. thumbnail, but not by namespace.
This is not always a valid XML, because xmlns namespace is not always declared. It is loaded to XElement this way:
XElement _root = XDocument.Parse(xmlString).Root;

You can try using this XPath expression to select all element in specific namespace :
//*[namespace-uri()='http://uri-address-here']
For example in C# :
var xml = #"<item xmlns:media='http://www.foo.org/'>
<title>lorem</title>
<description>ipsum</description>
<media:category>Sports</media:category>
<media:title>Combo</media:title>
<media:thumbnail url='http://dolor/0.jpg' height='100' width='200'/>
<media:thumbnail url='http://sit/0.jpg' height='300' width='400'/>
</item>";
var element = XElement.Parse(xml);
var elements = element.XPathSelectElements("//*[namespace-uri()='http://www.foo.org/']");

Related

Access Child nodes with namespace using xpath

How can I read the content of the childnotes using Xpath?
I have already tried this:
var xml = new XmlDocument();
xml.Load("server-status.xml");
var ns = new XmlNamespaceManager(xml.NameTable);
ns.AddNamespace("ns", "namespace");
var node = xml.SelectSingleNode("descendant::ns:server[ns:ip-address]", ns)
Console.WriteLine(node.InnerXml)
But I only get a string like this:
<ip-address>127.0.0.50</ip-address><name>Server 1</name><group>DATA</group>
How can I get the values individually?
Xml file:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<server-status xmlns="namespace">
<server>
<ip-address>127.0.0.50</ip-address>
<name>Server 1</name>
<group>DATA</group>
</server>
</server-status>
You're using XML namespaces in XPath correctly.
However, your original XPath,
descendant::ns:server[ns:ip-address]
says to select all ns:server elements with ns:ip-address children.
If you wish to select the ns:ip-address children themselves, instead use
descendant::ns:server/ns:ip-address
Similarly, you could select ns:name or ns:group elements.

Read XML element value by excluding namespace related attributes present in the XML

I am trying to read the values of the elements present in XML. It is working properly if input doesn't contains any namespaces related attributes in that XML as below -
string testInputXML = #"
<FIXML>
<Header>
<RequestHeader>
<MessageKey>
<RequestUUID>Req_1499940064961</RequestUUID>
<ServiceRequestId>getLastNTransactionsWithPagination</ServiceRequestId>
<ServiceRequestVersion>10.2</ServiceRequestVersion>
<ChannelId>COR</ChannelId>
<LanguageId></LanguageId>
</MessageKey>
</RequestHeader>
</Header>
<isLogging>true</isLogging>
</FIXML>
";
XElement myElement = XElement.Parse(testInputXML);
Console.WriteLine(myElement.Element("isLogging").Value);
If input XML contains any namespaces related attributes then it is throwing exception.
Object reference not set to an instance of an object.
string testInputXML = #"
<FIXML xsi:schemaLocation=""http://www.w3.org/2001/XMLSchema-instance"" xmlns=""http://www.w3.org/fixml"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"">
<Header>
<RequestHeader>
<MessageKey>
<RequestUUID>Req_1499940064961</RequestUUID>
<ServiceRequestId>getLastNTransactionsWithPagination</ServiceRequestId>
<ServiceRequestVersion>10.2</ServiceRequestVersion>
<ChannelId>COR</ChannelId>
<LanguageId></LanguageId>
</MessageKey>
</RequestHeader>
</Header>
<isLogging>true</isLogging>
</FIXML>
";
XElement myElement = XElement.Parse(testInputXML);
Console.WriteLine(myElement.Element("isLogging").Value);
Is there any way to exclude the namespaces present in the XML while fetching element values?
Try this:
Console.WriteLine(myElement.DescendantsAndSelf().Elements().FirstOrDefault(d => d.Name.LocalName == "isLogging")?.Value);
Try using XPath like this:
XDocument xmlDoc = XDocument.Parse(xml);
XElement element = xmlDoc.XPathSelectElement("/*[name()='FIXML']/*[name()='isLogging']");

Remove xmlns attribute in xml so as to get simple plain xml node

I am using the following code
var xmlFile = fromConfiguration + #"xyz.xml";
XDocument document = XDocument.Load(xmlFile);
var xElement = document.Root;
xElement.Add(new XElement("elementName", "elementText"));
document.Save(xmlFile);
XDocument documentN = XDocument.Load(xmlFile);
XElement element = (from xml2 in documentN.Descendants("elementName")
select xml2).FirstOrDefault();
element.RemoveAttributes();
documentN.Save(xmlFile);
This gives me..
<elementName xmlns="">elementText</elementName>
xmlns is added by default.
Is there any way I can add without xmlns?
<elementName>elementText</elementName>
This is what I need to parse in my xsl file.
ANy help ??
One of the ancestor elements must be setting a default namespace, e.g.
<foo xmlns="http://foo.bar">
<!-- Your element name -->
</foo>
If you want:
<foo xmlns="http://foo.bar">
<elementName>elementText</elementName>
</foo>
... then that means elementName is implicitly in a namespace of http://foo.bar, because the default is inherited. So you should use:
XNamespace ns = "http://foo.bar";
xElement.Add(new XElement(ns + "elementName", "elementText"));
If you might have different namespaces in different files, you could determine the default namespace programmatically - although it may not be the namespace of the root element, e.g.
<other:foo xmlns="http://foo.bar" xmlns:other="http://surprise">
<!-- This is still in http://foo.bar -->
<elementName>elementText</elementName>
</foo>
Usually it is better to learn how to find or select elements in a namespace and how to construct them thus if you do
xElement.Add(new XElement(xElement.Name.Namespace + "elementName", "elementText"));
you don't have the problem of xmlns="''.

XDocument.XPathSelectElements : I can't seem get the xpath syntax right

I'm trying to work out why my xpath won't select the nodes I specificy
My xpath expression is //DefaultValue, so I expect all elements of name DefaultValue to be selected
My test file (cut down) is :
<?xml version="1.0" encoding="utf-8"?>
<SharedDataSet xmlns:rd="http://schemas.microsoft.com/SQLServer/reporting/reportdesigner" xmlns="http://schemas.microsoft.com/sqlserver/reporting/2010/01/shareddatasetdefinition">
<Description />
<DataSet Name="ddd">
<Query>
<DataSourceReference>xxx</DataSourceReference>
<DataSetParameters>
<DataSetParameter Name="p1">
<DefaultValue>baaaah</DefaultValue> <!-- this node should be selected eh? -->
</DataSetParameter>
<DataSetParameter Name="p2">
<DefaultValue>fooo</DefaultValue> <!-- this node should be selected too eh? -->
</DataSetParameter>
</DataSetParameters>
</Query>
</SharedDataSet>
Code is :
XmlNamespaceManager xn = new XmlNamespaceManager(new NameTable());
xn.AddNamespace("ns", "http://schemas.microsoft.com/sqlserver/reporting/2010/01/shareddatasetdefinition");
xn.AddNamespace("rd", "http://schemas.microsoft.com/SQLServer/reporting/reportdesigner");
xn.AddNamespace("cl", "http://schemas.microsoft.com/sqlserver/reporting/2010/01/componentdefinition");
XDocument document = XDocument.Parse(reportBuffer, LoadOptions.PreserveWhitespace);
IEnumerable<XElement> elements = document.XPathSelectElements(xpath, xn);
at this point elements.Count() equals 0
Can anyone see what foolishness I present?
I can't seem to make the xml demons happy... :-(
The <DefaultValue> element is actually bound to the namespace http://schemas.microsoft.com/sqlserver/reporting/2010/01/shareddatasetdefinition.
The <SharedDataSet> document element has it declared without a prefix, so it is easy to miss. Since <DefaultValue> is a descendant, it inherits the namespace.
If you want to select the <DefaultValue> element you need to adjust your XPath:
//ns:DefaultValue

How to query XElement with two namespaces

I'm trying to find the inner text value of an element using LINQ-to-XML (an XElement object). I make my service call and get an XML response back that I've successfully loaded into an XElement object. I want to extract the inner text of one of the elements - however, every time I try to do this, I get a null result.
I feel like I'm missing something super-simple, but I'm fairly new to LINQ-to-XML. Any help is appreciated.
I'm trying to get the inner text value of the StatusInfo/Status element. Here's my XML document that's returned:
<feed xml:lang="en-us" xmlns="http://www.w3.org/2005/Atom">
<title type="text">My Response</title>
<id>tag:foo.com,2012:/bar/06468dfc-32f7-4650-b765-608f2b852f22</id>
<author>
<name>My Web Services</name>
</author>
<link rel="self" type="application/atom+xml" href="http://myServer/service.svc/myPath" />
<generator uri="http://myServer" version="1">My Web Services</generator>
<entry>
<id>tag:foo.com,2012:/my-web-services</id>
<title type="text" />
<updated>2012-06-27T14:22:42Z</updated>
<category term="tag:foo.com,2008/my/schemas#system" scheme="tag:foo.com,2008/my/schemas#type" />
<content type="application/vnd.my.webservices+xml">
<StatusInfo xmlns="tag:foo.com,2008:/my/data">
<Status>Available</Status> <!-- I want the inner text -->
</StatusInfo>
</content>
</entry>
</feed>
Here's a snippet of code that I'm using to extract the value (which doesn't work):
XElement root = XElement.Load(responseReader);
XNamespace tag = "tag:foo.com,2008:/my/data";
var status = (from s in root.Elements(tag + "Status")
select s).FirstOrDefault();
My status variable is always null. I've tried several variations on this, but to no avail. The part that's confusing me is the namespace -- tag and 2008 are defined. I don't know if I'm handling this correctly or if there's a better way to deal with this.
Also, I don't have control over the XML schema or the structure of the XML. The service I'm using is out of my control.
Thanks for any help!
Try Descendants() instead of Elements():
XElement x = XElement.Load(responseReader);
XNamespace ns = "tag:foo.com,2008:/my/data";
var status = x.Descendants(ns + "Status").FirstOrDefault().Value;
There are 2 Namespaces in the feed:
the Atom namespace
the tag namespace
The outer xml needs to use the Atom namespace, while a portion of the inner xml needs to use the tag namespace. i.e.,
var doc = XDocument.Load(responseReader);
XNamespace nsAtom = "http://www.w3.org/2005/Atom";
XNamespace nsTag = "tag:foo.com,2008:/my/data";
// get all entry nodes / use the atom namespace
var entry = doc.Root.Elements(nsAtom + "entry");
// get all StatusInfo elements / use the atom namespace
var statusInfo = entry.Descendants(nsTag + "StatusInfo");
// get all Status / use the tag namespace
var status = statusInfo.Elements(nsTag + "Status");
// get value of all Status
var values = status.Select(x => x.Value.ToString()).ToList();

Categories

Resources