Reading XML with Namespace in Windows 8 Store App - c#

I'm working on an WinRT App and I should ready some XML with a namespace in it. Now this was ok with the old .NET, but now there is no XmlDocument.NameTable anymore. So how can I create an XmlNamespaceManager?
var XmlDoc = new XmlDocument();
XmlDoc.Load(new FileStream("XMLFile1.xml",FileMode.Open,FileAccess.Read));
var nsm = new XmlNamespaceManager(XmlDoc.NameTable);
nsm.AddNamespace("s", "http://api.facebook.com/1.0/");

It's much easier to handle namespaces with LINQ to XML, which I believe is what you can use in Windows 8:
XDocument doc = XDocument.Load(...);
XNamespace ns = "http://api.facebook.com/1.0/";
XElement element = doc.Root.Element(ns + "foo");
Read a good LINQ to XML introduction or tutorial, and you should see how to handle namespaces - but it really is significantly simpler than with XmlDocument. You simply don't need namespace managers any more! (There may be some cases where they're still useful - I'm not sure - but I certainly can't remember using them myself with LINQ to XML.)

Related

Namespaces, Schemas, Elements and Attributes in an XmlDocument in .NET

I'm putting this here because I saw a lot of Q&A for XML on StackOverflow while trying to solve my own problems, and figured that once I'd found it, I'd post what I found so when someone else needs some XML help, this might help them.
My goal: To create an XML document that contains the following XML Declaration, Schema & Namespace Information:
<?xml version="1.0" encoding="UTF-8"?>
<abc:abcXML xsi:schemaLocation="urn:abcXML:v12 http://www.test.com/XML/schemas/v12/abcXML_v12.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ase="urn:abcXML:v12">
I'd already done it in Python for a quick prototype using minidom, and it was very simple. I needed to do it in a .NET language though (C#), because that's what the business calls for. I'm quite familiar with C#, but I've always stayed away from processing XML with it because I honestly don't have an in-depth grasp of XML and it's guidelines. Today, I had to face my demons.
Here's how I did it:
The first part is simple enough - create a document, and create a DocumentElement for the root (there's a catch here which I get to later):
XmlDeclaration xmlDeclaration = doc.CreateXmlDeclaration("1.0", "UTF-8", null);
XmlElement root = doc.DocumentElement;
doc.InsertBefore(xmlDeclaration, root);
The next part seems simple enough - create an element, give it a prefix, name and URI, then append it to the document. I thought this would work, but it doesn't (this is where the minimal understanding of XML comes into play):
XmlElement abcXML = xmlDoc.CreateElement("ase", "abcXML", "urn:abcXML:r38 http://www.w3.org/2001/XMLSchema-instance");
XmlAttribute xmlAttr = xmlDoc.CreateAttribute("xsi:schemaLocation", "urn:abcXML:v12 http://www.test.com/XML/schemas/v12/abcXML_v12.xsd");
abcXML.AppendChild(xmlAttr);
xmlDoc.AppendChild(abcXML);
I tried to use doc.LoadXml() and doc.CreateDocumentFragment() and write my own declarations. No - I would get "Unexpected end of file". For those interested in XmlDocumentFragment: https://learn.microsoft.com/en-us/dotnet/api/system.xml.xmldocumentfragment.innerxml?view=netcore-3.1
This Microsoft article about XML Schemas and Namespaces didn't directly help me: https://learn.microsoft.com/en-us/dotnet/standard/data/xml/including-or-importing-xml-schemas
After doing more reading on XML, and going through the documentation for XmlDocument, XmlElement and XmlAttribute, this is the solution:
XmlElement abcXML = xmlDoc.CreateElement("ase", "abcXML", "urn:abcXML:r38");
XmlAttribute xmlAttr = xmlDoc.CreateAttribute("xsi:schemaLocation", "http://www.w3.org/2001/XMLSchema-instance");
xmlAttr.InnerXml = "urn:abcXML:v12 http://www.test.com/XML/schemas/v12/abcXML_v12.xsd";
abcXML.Attributes.Append(xmlAttr);
xmlDoc.AppendChild(abcXML);
Now you can add the elements to your document like so:
XmlElement header = doc.CreateElement(string.Empty, "Header", string.Empty);
abcXML.AppendChild(header);
To save the document, I used:
xmlDoc.Save(fileLocation);
I compared my output to the sample I had, and after comparing the file contents, I had succeeded in matching it. I provided the output to the client, they uploaded it into application they were using, and it failed: Row 1, Column 1 - Unexpected Character.
I had a suspicion it was encoding, and I was right. Using xmlDoc.Save(fileLocation) is correct, but it generates a UTF-8 file with the Byte Order Mark (BOM) at Row 1, Column 1. The XML parsing function in the application doesn't expect that, so the process failed. To fix that, I used the following method:
Encoding enc = new UTF8Encoding(false); /* This creates a UTF-8 encoding without the BOM */
using (System.IO.TextWriter tw = new System.IO.StreamWriter(filePath, false, enc))
{
xmlDoc.Save(tw);
}
return true;
I generated the file again, sent it to the client, and it worked first go.
I hope someone finds this to be useful.
For complicated namespaces it is simpler to just parse the xml string. I like using xml linq. You sample xml is wrong. The namespace is "ase" (not abc).
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<ase:abcXML xsi:schemaLocation=\"urn:abcXML:v12 http://www.test.com/XML/schemas/v12/abcXML_v12.xsd\"" +
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" +
" xmlns:ase=\"urn:abcXML:v12\">" +
"</ase:abcXML>";
XDocument doc = XDocument.Parse(xml);
XElement root = doc.Root;
XNamespace nsAse = root.GetNamespaceOfPrefix("ase");
}
}
}

XPath to XBRL files

I am trying to extract some information using XPath from an XBRL file (eXtensible Business Reporting Language), which is basically just an XML file.
Here is an example file
The file has multiple namespace declarations and these declarations change from file to file, sometimes.
Can you please help to write the XPath to extract the data in the node "dei:EntityRegistrantName", using C#?
I have tried multiple articles on the internet but can't figure this out.
Using this XML Library, I use a simple element get. The library figures out the namespace for me:
XElement root = XElement.Load(file); // or .Parse(string)
var a = root.XPathElement("//dei:EntityRegistrantName");
Console.WriteLine(a.ToString());
The output is (formatted for readability):
<dei:EntityRegistrantName
contextRef="eol_PE8528----1510-K0009_STD_365_20150630_0"
id="id_6568047_FBD9ABEE-63B9-43BD-B87B-EFE7CC59EFB0_1_400001"
xmlns:dei="http://xbrl.sec.gov/dei/2014-01-31">
MICROSOFT CORPORATION
</dei:EntityRegistrantName>
Simply use the query methods available to you with LINQ to XML:
var doc = XDocument.Load(file);
Namespace dei = "http://xbrl.sec.gov/dei/2014-01-31"
var name = (string)doc.Descendants(dei + "EntityRegistrantName").Single();

Is it possible to reload an XDocument and keep all existing references to it?

I've got an XDocument created via XDocument's static Load method (taking an XmlReader) as follows:
XDocument doc;
doc = XDocument.Load(reader);
Now, it's necessary at some point to reload the document (restoring to the original version after changes have been made). The obvious way to do this seems to be doc = XDocument.Load(reader);. However, this will create a new XDocument, and any existing references to doc will still point at the old (altered) version despite the fact that we've (re)loaded the original.
Is there a way to load an XmlReader (or even a string or byte[] representation of XML) into an existing XDocument, overwriting the contents? Or would I have to make all of the changes (dropping its elements and adding new ones) manually?
You should be able to do
doc.Root.ReplaceWith( XElement.Load(fileName));
If you want to preserve processing instructions you may need to Load into a temp XDocument first.
You can replace the root of the XDocument.
var oldDoc = new XDocument();
oldDoc.Add(new XElement("OldRoot"));
var newDoc = new XDocument();
newDoc.Add(new XElement("NewRoot"));
oldDoc.Root.ReplaceWith(newDoc.Root);

Find out the default namespace URI from an XML document in C#

Some other questions have asked how to use Xpath to query XML documents with a default namespace. The answer is to use a namespace manager to create an alias for the default namespace, and use that alias in your xpaths.
However, what if you don't know the URI of the default namespace in advance? How do you find it out from the XML document?
var doc = XDocument.Parse(myXml);
XNamespace ns = doc.Root.GetDefaultNamespace();
If you are using XmlDocument, you can get the default namespace by checking NamespaceURI of the root element:
var document = new XmlDocument();
document.LoadXml("<root xmlns='http://java.sun.com/xml/ns/j2ee'></root>");
var defaultNamespace = document.DocumentElement.NamespaceURI;
Assert.IsTrue(defaultNamespace == "http://java.sun.com/xml/ns/j2ee");
You could try using XmlNamespaceManager.DefaultNamespace to get it.
http://msdn.microsoft.com/en-us/library/system.xml.xmlnamespacemanager.defaultnamespace.aspx
I know this is an old topic, but I had the same problem, using the XmlDocument class, as I wanted to know the Default Namespace and a prefixed namespace.
I could get both namespaces using the same Method.
string prefixns = element.GetNamespaceOfPrefix("prefix");
string defaultns = element.GetNamespaceOfPrefix("");
this seems to work for me getting both namespaces on a XmlElement.
Edit: This is a XmlNode Method, so should also work on Attributes
The simplest way to do it
XmlDocument xDoc = new XmlDocument();
xDoc.Load(uriPath);
Console.WriteLine(xDoc.NamespaceURI);

No Nodes Selected from Atom XML document using XPath?

I'm trying to parse an Atom feed programmatically. I have the atom XML downloaded as a string. I can load the XML into an XmlDocument. However, I can't traverse the document using XPath. Whenever I try, I get null.
I've been using this Atom feed as a test: http://steve-yegge.blogspot.com/feeds/posts/default
Calling SelectSingleNode() always returns null, except for when I use "/". Here is what I'm trying right now:
using (WebClient wc = new WebClient())
{
string xml = wc.DownloadString("http://steve-yegge.blogspot.com/feeds/posts/default");
XmlNamespaceManager nsMngr = new XmlNamespaceManager(new NameTable());
nsMngr.AddNamespace(string.Empty, "http://www.w3.org/2005/Atom");
nsMngr.AddNamespace("app", "http://purl.org/atom/app#");
XmlDocument atom = new XmlDocument();
atom.LoadXml(xml);
XmlNode node = atom.SelectSingleNode("//entry/link/app:edited", nsMngr);
}
I thought it might have been because of my XPath, so I've also tried a simple query of the root node since I knew the root should work:
// I've tried both with & without the nsMngr declared above
XmlNode node = atom.SelectSingleNode("/feed");
No matter what I do, it seems like it can't select anything. Obviously I'm missing something, I just can't figure out what. What is it that I need to do in order to make XPath work on this Atom feed?
EDIT
Although this question has an answer, I found out this question has an almost exact duplicate: SelectNodes not working on stackoverflow feed
While the C# implementation may allow default namespaces (I don't know), the XPath 1.0 spec doesn't. So, give "Atom" its own prefix:
nsMngr.AddNamespace("atom", "http://www.w3.org/2005/Atom");
And change your XPath appropriately:
XmlNode node = atom.SelectSingleNode("//atom:entry/atom:link/app:edited", nsMngr);
Load XML from a string and lookup for any 'Errors/Error' nodes.
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xmlResult);
XmlNamespaceManager nm = new XmlNamespaceManager(xmlDoc.NameTable);
nm.AddNamespace("ns", "http://somedomain.com/namespace1/2"); //ns - any name, make sure it is same in the below line
XmlNodeList errors = xmlDoc.SelectNodes("/ns:*//ns:Errors/ns:Error", nm);
-Mathulan

Categories

Resources