XPath to XBRL files - c#

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();

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");
}
}
}

Can't load files that has xmlns attribute in the first child with XDocument

I am new to XDocument but I have been looking around for a solution to this problem which I couldn't get to fix.
I need to load some kind of XML files (PNML) that comes this way:
<pnml xmlns="http://www.pnml.org/version-2009/grammar/pnml">
<net id="id" type ="http://www.pnml.org/version-2009/grammar/ptnet">
..........</net> </pnml>
And I couldn't get to load these kind of files unless I add "xmlns" as an Attribute to the node net .
Meanwhile, the files I create myself has this xmlns attribute, and I can load them without problems.
While, files that are generated from some other software that I need to be able to use from my software doesn't has this "xmlns" attribute, and if I add it myself to the files generated by this software, I can load those files.
Here's the code I am using to Load :
XDocument doc = XDocument.Load(file);
XNamespace ns = #"http://www.pnml.org/version-2009/grammar/pnml";
foreach (XElement element in doc.Element(ns + "pnml")
.Elements("net").Elements("page").Elements("place"))
{ // Do my loading to "place" nodes for example }
But whenever I try to load a file, it just skips my "foreach" statement, and if I add some line before "foreach" like:
string id= (string) doc.Element(ns + "pnml")
.Element("net").Attribute("id");
it says:
Object reference not set to an instance of an object.
Here's an example of a file generated by my code and also can be read from my code:
<?xml version="1.0" encoding="utf-8"?>
<pnml xmlns="http://www.pnml.org/version-2009/grammar/pnml">
<net id="netid" type="http://www.pnml.org/version-2009/grammar/ptnet" xmlns="">
nodes and information </net> </pnml>
NOTE: I use this code to save my files:
XNamespace ns = #"http://www.pnml.org/version-2009/grammar/pnml";
XDocument doc = new XDocument
(
new XElement(ns+"pnml"
, new XElement("net",new XAttribute("id", net_id), ...));
I found a way to save my files without this "xmlns" attribute, but once I omit it, I can't load it from my code. And the first example I wrote is the standard format and I really need to get ride of the "xmlns" problem.
EDIT: I'm sorry if you got confused, what I want is to be able to load the standard PNML files that doesn't have thise "xmlns" attribute within the "net" node.
What you're missing is that element namespaces are inherited from their parents.
So your XML:
<pnml xmlns="http://www.pnml.org/version-2009/grammar/pnml">
<net id="id" type ="http://www.pnml.org/version-2009/grammar/ptnet">
...
Contains two elements. One is pnml with the namespace http://www.pnml.org/version-2009/grammar/pnml, and the child is net which also has the namespace http://www.pnml.org/version-2009/grammar/pnml.
With this in mind, your query on the existing XML should be:
doc.Element(ns + "pnml").Elements(ns + "net")...
And your code to generate the XML should be:
new XElement(ns + "pnml",
new XElement(ns + "net", new XAttribute("id", net_id), ...));
Try something like this
var result = doc.Element(ns + "pnml").Descendants().Where(x=>x.Name.LocalName=="net")

c# get local path of .xsd from xsi:schemaLocation attribute value

I am looking for a best way to exctract a local path of the schema without using regex.
Sample:
<?xml version="1.0"?>
<ord:order xmlns:ord="http://example.org/ord"
xmlns:prod="http://example.org/prod"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://example.org/prod chapter05prod.xsd
http://example.org/ord chapter05ord.xsd">
<items>
<prod:product>
<number xsi:type="xs:short">557</number>
<name>Short-Sleeved Linen Blouse</name>
<size xsi:nil="true"/>
</prod:product>
</items>
or
xsi:schemaLocation="http://example.org/prod \\RandomFolder\New\chapter05prod.xsd">
or
xsi:schemaLocation="chapter05prod.xsd">
I would like to get a local path for *.xsd file. Is there any way to do this using a xml parser or xmlResolver or in some other way that is not using a regex?
Edit: I am looking for a most generic approach to get a path for external .xsd path references.
Another example:
xsi:noNamespaceSchemaLocation="file://C://Documents and Settings//All Users//Application Data//My Application//MyData.xsd"
You can use XPath:
using System;
using System.Xml;
using System.Xml.XPath;
Since the data you want is inside an attribute that is qualified by a namespace, you would need to register the namespace before performing an XPath expression. In your case, you can ignore the namespace and use an expression like this one:
//#*[local-name()='schemaLocation']
which will select the attribute node which has a local name of schemaLocation (ignoring its prefix).
Parse your XML file and get the root (document) element:
XmlTextReader reader = new XmlTextReader("your-file.xml");
XmlDocument doc = new XmlDocument();
doc.Load(reader);
reader.Close();
XmlElement root = doc.DocumentElement;
Then use it to select all attributes named schemaLocation. There is only one, so you can use SelectSingleNode:
XmlNode schemaLocationAttribute = root.SelectSingleNode("//#*[local-name()='schemaLocation']");
The expression above contains the attribute. You can get its string contents using schemaLocationAttribute.Value. From there you can split the contents using whitespace as the delimiter:
string[] components = schemaLocationAttribute.Value.Split(null);
And you will have the text that you want (chapter05prod.xsd) in components[1]:
Console.WriteLine (components[1]);
(Note: you can't always ignore XPath namespaces - if there were other attributes named schemaLocation in your file with a different prefix or with no prefix, they would also be selected by that XPath expression and this solution would fail.)

How to comment an XMLElement using C#?

My application reads values from an xml file which I write everytime when I execute the application.
This is how I made comments of my lines:
XmlComment DirCom = doc.CreateComment("Comment")
XmlElementName.AppendChild(DirCom);
And It works fine, But now I need to comment the XML element, And the above doesn't work. My final result should be like:
<!--<name>David</name>-->
using C# and the XML document library.
To comment an xml node I would do it like this :
XmlComment DirCom = doc.CreateComment(XmlElementName.OuterXml);
doc.InsertAfter(DirCom, XmlElementName);
doc.RemoveChild(XmlElementName)

How to create the XDocument instance for loading the XML file after deserializing the the object?

I am developing window phone 7 application. I am new to the window phone 7 application. I am referring to the following link for XML Serialization & Deserialization.
http://www.codeproject.com/KB/windows-phone-7/wp7rssreader.aspx
In the above link the LoadFromIso() function is used for XML Deserialization. I want to load the xml file after deserialization in the above link. In simple one case we can do this as in the following code. Similar to the following code I want "doc" in the above link. In the following code we can perform the various opeations on the XML file by using LINQ to XML with following statement
doc = XDocument.Load(isfStream);
The complete code is as follows
IsolatedStorageFile isfData = IsolatedStorageFile.GetUserStoreForApplication();
XDocument doc = null;
IsolatedStorageFileStream isfStream = null;
if (isfData.FileExists(strXMLFile))
{
isfStream = new IsolatedStorageFileStream(strXMLFile, FileMode.Open, isfData);
doc = XDocument.Load(isfStream);
isfStream.Close();
}
In the similar way I want the instance of the XDocument after deserializing the object so that I can perform the various operations on the XML file by using LINQ to XML. Can you please provide me any code or link through which I can obtain the instance of the XDocument so that I can load the XML file & perform the various operation on the XML file by using the LINQ to XML ?
The variable doc in your code is an XDocument of the deserialized content.
You can perform your operations on/with doc.
A simple WP7 project demonstrating loading XML using XDocument and LINQ and data binding to a listbox here. As Matt advises the work gets done on your XDocument instance.
binding a Linq datasource to a listbox

Categories

Resources