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

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.)

Related

How do I edit Node Values in an Xml File with C#

I am trying to change the values in a Farming simulator 22 savegame xml file from C# in visual studio. There are a lot of nodes so I have reduced them to make things easier. I want to know how to replace the value in the node using C# with out having to create and rebuild the xml file from scratch.
the path to the xml file is: (C:\Users\Name\Documents\My Games\FarmingSimulator2022\savegame1\careerSavegame.xml)
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<careerSavegame revision="2" valid="true">
<settings>
<savegameName>My game save</savegameName>
<creationDate>2022-05-03</creationDate>
<mapId>MapFR</mapId>
<mapTitle>Haut-Beyleron</mapTitle>
<saveDateFormatted>2022-08-22</saveDateFormatted>
<saveDate>2022-08-22</saveDate>
<resetVehicles>false</resetVehicles>
</careerSavegame>
You can use the System.Xml.Linq namespace to access the xml file. This will load the file in the memory.
There is one class inside it, XDocument, that represents the xml document.
String filePath = "C:\Users\Name\Documents\My Games\FarmingSimulator2022\savegame1\careerSavegame.xml"
XDocument xdoc = XDocument.Load(filePath);
var element = xdoc.Elements("MyXmlElement").Single();
element.Value = "foo";
xdoc.Save("file.xml");
You can set the element variable as per the one which is needed to be replaced.
Through some research I found the solution to editing the values within the nodes. In this example I only change the value of savegameName, but it will be the same for the rest.
//Routing the xml file
XmlDocument xmlsettings = new XmlDocument();
xmlsettings.Load(#"D:\careerSavegame.xml");
//Setting values to nodes through innertext
String FarmNameSetting = "Martek Farm";
XmlNode savegameNamenode =
xmlsettings.SelectSingleNode
("careerSavegame/settings/savegameName");
savegameNamenode.InnerText = FarmNameSetting;

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

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

Which XPath expression should I use for XmlDocument.SelectSingleNode when the document has no namespace?

I have some XML that has been generated by default conversion of a JSON response stream, and so doesn't have a namespace declared. I now want to retrieve a specific node from that XML by using the SelectSingleNode method, but cannot specify the namespace because there isn't one specified. What should I use to register a namespace?
My XML looks like this:
<root type="object">
<customer type="object">
<firstName type="string">Kirsten</firstName>
<lastName type="string">Stormhammer</lastName>
</customer>
</root>
The code I have tried is:
XmlDocument document = new XmlDocument();
document.LoadXml(customerXml);
XmlNamespaceManager manager = new XmlNamespaceManager(document.NameTable);
manager.AddNamespace("x", "http://www.w3.org/TR/html4/"); // What should I use here?
XmlNode customerNode= document.SelectSingleNode("x:customer");
This always returns null.
I have also tried using the local-name qualifier (without using a namespace manager):
XmlDocument document = new XmlDocument();
document.LoadXml(customerXml);
XmlNode customerNode= document.SelectSingleNode("/*[local-name()='root']/*[local-name()='customer']");
This also returns null.
Then you can do in a much simpler way, without involving XmlNamespaceManager and namespace prefix :
XmlDocument document = new XmlDocument();
document.LoadXml(customerXml);
XmlNode customerNode= document.SelectSingleNode("/root/customer");
[.NET fiddle demo]

XmlWriter writing empty xmlns

I'm using the following code to initialise an XmlDocument
XmlDocument moDocument = new XmlDocument();
moDocument.AppendChild(moDocument.CreateXmlDeclaration("1.0", "UTF-8", null));
moDocument.AppendChild(moDocument.CreateElement("kml", "http://www.opengis.net/kml/2.2"));
Later in the process I write some values to it using the following code
using (XmlWriter oWriter = oDocument.DocumentElement.CreateNavigator().AppendChild())
{
oWriter.WriteStartElement("Placemark");
//....
oWriter.WriteEndElement();
oWriter.Flush();
}
This ends up giving me the following xml when I save the document
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<Placemark xmlns="">
<!-- -->
</Placemark>
</kml>
How can I get rid of the empty xmlns on the Placemark element?
--EDITED TO SHOW CHANGE TO HOW PLACEMARK WAS BEING WRITTEN--
If I put the namespace in the write of placemark then non of the elements are added to the document.
I have fixed the issue by creating the document with the following code (no namespace in the document element)
XmlDocument moDocument = new XmlDocument();
moDocument.AppendChild(moDocument.CreateXmlDeclaration("1.0", "UTF-8", null));
moDocument.AppendChild(moDocument.CreateElement("kml"));
And by saving it with the following code to set the namespace before the save
moDocument.DocumentElement.SetAttribute("xmlns", msNamespace);
moDocument.Save(msFilePath);
This is valid as the namespce is only required in the saved xml file.
This is an old post, but just to prevent future bad practice; you should never declare the xmlns namespace in an XML document, so this may be the cause why you get empty nodes since you are doing something the XmlDocument is not supposed to do.
The prefix xmlns is used only to declare namespace bindings and is by
definition bound to the namespace name http://www.w3.org/2000/xmlns/.
It MUST NOT be declared . Other prefixes MUST NOT be bound to this
namespace name, and it MUST NOT be declared as the default namespace.
Element names MUST NOT have the prefix xmlns.
Source: http://www.w3.org/TR/REC-xml-names/#ns-decl
The following code worked for me (source):
XmlSerializer s = new XmlSerializer(objectToSerialize.GetType());
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("","");
s.Serialize(xmlWriter, objectToSerialize, ns);
oWriter.WriteStartElement("Placemark"); should work, because the parent node already has the right namespace.
Did you try:
oWriter.WriteStartElement("kml", "Placemark", "kml");
You needed
oWriter.WriteStartElement("Placemark", "http://www.opengis.net/kml/2.2");
otherwise the Placemark element gets put in the null namespace, which is why the xmlns="" attribute is added when you serialize the XML.

Categories

Resources