XML SelectSingleNode Namespacing issue - c#

I'm trying to get a single value from an XML using the SelectSingleNode, but it keeps returning null on me.
I've been looking here on SO and it seems it has something to do with the namespace. I tried adding it but I keep getting null.
The XML looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<EML xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xnl="urn:oasis:names:tc:ciq:xnl:4"
xmlns:xal="urn:oasis:names:tc:ciq:xal:4"
xmlns="urn:oasis:names:tc:evs:schema:eml"
xmlns:martine="http://www.martine.be/extensions"
Id="510"
SchemaVersion="7.0"
xsi:schemaLocation="urn:oasis:names:tc:evs:schema:eml schema/510-count-v7-0.xsd
http://www.martine.be/extensions schema/martine-eml-extensions.xsd">
<EMLHeader>
<TransactionId>01</TransactionId>
<ManagingAuthority>
<AuthorityIdentifier>2</AuthorityIdentifier>
<AuthorityName>
<NameElement ElementType="">VLR</NameElement>
</AuthorityName>
<Description>Some Description</Description>
<OrganizationURL>Unknown</OrganizationURL>
<AuthorityAddress/>
</ManagingAuthority>
</EMLHeader>
I'm trying to extract the Description using the code below:
XmlDocument doc = new XmlDocument();
doc.LoadXml(content);
var nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("ns", "urn:oasis:names:tc:evs:schema:eml");
XmlNode testNode = doc.SelectSingleNode("/ns:EML/ns:EMLHeader/ns:ManagingAuthority/ns:Description", nsmgr);
if (testNode != null)
{
Console.WriteLine(testNode.InnerText);
}
What am doing wrong?

Tested this and you are missing closing </EML> tag. This was the error I got
Unhandled Exception: System.Xml.XmlException: Unexpected end of file has occurred. The following elements are not closed: EML. Line 24, position 17.
TestCodeApp.cs
using System;
using System.Xml;
public class Program
{
public static void Main()
{
XmlDocument doc = new XmlDocument();
doc.Load("input.xml");
var nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("ns", "urn:oasis:names:tc:evs:schema:eml");
XmlNode testNode = doc.SelectSingleNode("/ns:EML/ns:EMLHeader/ns:ManagingAuthority/ns:Description", nsmgr);
if (testNode != null)
{
Console.WriteLine(testNode.InnerText);
}
}
}
input.xml
<?xml version="1.0" encoding="UTF-8"?>
<EML xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xnl="urn:oasis:names:tc:ciq:xnl:4"
xmlns:xal="urn:oasis:names:tc:ciq:xal:4"
xmlns="urn:oasis:names:tc:evs:schema:eml"
xmlns:martine="http://www.martine.be/extensions"
Id="510"
SchemaVersion="7.0"
xsi:schemaLocation="urn:oasis:names:tc:evs:schema:eml schema/510-count-v7-0.xsd
http://www.martine.be/extensions schema/martine-eml-extensions.xsd">
<EMLHeader>
<TransactionId>01</TransactionId>
<ManagingAuthority>
<AuthorityIdentifier>2</AuthorityIdentifier>
<AuthorityName>
<NameElement ElementType="">VLR</NameElement>
</AuthorityName>
<Description>Some Description</Description>
<OrganizationURL>Unknown</OrganizationURL>
<AuthorityAddress/>
</ManagingAuthority>
</EMLHeader>
</EML>

Your code look OK, except:
you missed close node in the xml file: ""
if your content is contain information of above xml, then you can use doc.LoadXML(content), otherwise you should use doc.Load(fileName).
Your xml file should be:
<?xml version="1.0" encoding="UTF-8"?>
<EML xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xnl="urn:oasis:names:tc:ciq:xnl:4"
xmlns:xal="urn:oasis:names:tc:ciq:xal:4"
xmlns="urn:oasis:names:tc:evs:schema:eml"
xmlns:martine="http://www.martine.be/extensions"
Id="510"
SchemaVersion="7.0"
xsi:schemaLocation="urn:oasis:names:tc:evs:schema:eml schema/510-count-v7-0.xsd
http://www.martine.be/extensions schema/martine-eml-extensions.xsd">
<EMLHeader>
<TransactionId>01</TransactionId>
<ManagingAuthority>
<AuthorityIdentifier>2</AuthorityIdentifier>
<AuthorityName>
<NameElement ElementType="">VLR</NameElement>
</AuthorityName>
<Description>Some Description</Description>
<OrganizationURL>Unknown</OrganizationURL>
<AuthorityAddress/>
</ManagingAuthority>
</EMLHeader>
</EML>
And then you can read it:
XmlDocument doc = new XmlDocument();
doc.Load(fileName);
var nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("ns", "urn:oasis:names:tc:evs:schema:eml");
XmlNode testNode = doc.SelectSingleNode("/ns:EML/ns:EMLHeader/ns:ManagingAuthority/ns:Description", nsmgr);
if (testNode != null)
{
Console.WriteLine(testNode.InnerText);
}

Related

XElement.Load() and "undeclared prefix" exception

I'm trying to load xml file using Xelement.Load() method and in case of some files, I get "ditaarch" is an undeclared prefix exception. The content of such troublesome xml's are similar to this simplified version:
<?xml version="1.0" encoding="UTF-8"?>
<concept ditaarch:DITAArchVersion="1.3">
<title>Test Title</title>
<menucascade>
<uicontrol>text</uicontrol>
<uicontrol/>
</menucascade>
</concept>
I've tried to follow suggestions to manually add or ignore "ditaarch" namespace using xml namespace manager:
using (XmlReader reader = XmlReader.Create(#"C:\test\example.xml"))
{
NameTable nameTable = new NameTable();
XmlNamespaceManager nameSpaceManager = new XmlNamespaceManager(nameTable);
nameSpaceManager.AddNamespace("ditaarch", "");
XmlParserContext parserContext = new XmlParserContext(null, nameSpaceManager, null, XmlSpace.None);
XElement elem = XElement.Load(reader);
}
But it leads to same exception as before. Most probably the solution is trivial but I just can't see it :(
If anyone would be able to point me in the right direction, I would be most grateful.
The presented markup is not namespace well-formed XML so I don't think XElement or XDocument is an option as it doesn't support colons in names. You can parse it with a legacy new XmlTextReader("foo.xml") { Namespaces = false } however.
And you could use XmlDocument instead of XDocument or XElement and check for any empty elements with e.g.
XmlDocument doc = new XmlDocument();
using (XmlReader xr = new XmlTextReader("example.xml") { Namespaces = false })
{
doc.Load(xr);
}
Console.WriteLine("Number of empty elements: {0}", doc.SelectNodes("//*[not(*)][not(normalize-space())]").Count);

How to edit content of a tag in a xml file

Example
<?xml version="1.0" encoding="UTF-8"?>
<Settings>
<Tag1>XXXX</Tag1>
<Tag2>YYYY</Tag2>
<Tag3>true</Tag3>
<Tag4>ZZZZ</Tag4>
</Settings>
I want to edit only the contents of Tag3 without having to create another .xml file
You may edit your XML file like this:
XmlDocument doc = new XmlDocument();
doc.Load("D:\\somefile.xml");
XmlNode root = doc.DocumentElement;
XmlNode myNode = root.SelectSingleNode("Settings::Tag3");
myNode.Value = "blabla";
doc.Save("D:\\somefile.xml");

Using Xpath to select single node, when node contains namespace

I have following response for one of my SOAP request.
<?xml version="1.0" encoding="utf-8" ?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<UploadIsoResponse xmlns="http://localhost:8000/gw/">
<UploadIsoResult>false</UploadIsoResult>
<status>ISO File does not exist</status>
<md5>string</md5>
<days>9/18/2015 12:00:00 AM</days>
</UploadIsoResponse>
</soap:Body>
</soap:Envelope>
And I am using following code to parse it. However I am getting an error Object reference not set to an instance of an object. This is actually because of the resulting null value returned from the xpath. Please help me on parsing single node.
public void fill_response_data(string xml_buffer)
{
string TARGET_NAME_SPACE = "http://localhost:8000/gw/";
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xml_buffer);
XmlNamespaceManager ns = new XmlNamespaceManager(xmlDoc.NameTable);
ns.AddNamespace("msbld", TARGET_NAME_SPACE);
XmlNode md5_node = xmlDoc.SelectSingleNode("//msbld:UploadIsoResponse/md5", ns);
XmlNode md5_status_node = xmlDoc.SelectSingleNode("//msbld:UploadIsoResponse/status", ns);
txt_md5_checksum.Text = md5_node.InnerText;
txt_status.Text = md5_status_node.InnerText;
}
Exception : System.Xml.XmlDocumentAn unhandled exception of type 'System.NullReferenceException' occurred in IsoGateway.exe
Fixed the issue by adding name space in xpath and below is the updated snippet.
XmlNode md5_node = xmlDoc.SelectSingleNode("//msbld:UploadIsoResponse/msbld:md5", ns);
XmlNode md5_status_node = xmlDoc.SelectSingleNode("//msbld:UploadIsoResponse/msbld:status", ns);

How to working with xml nodes

I have program with response from website in the xml format with namespace.
example program:
string responsedata;//response data from website
//Creat new XMLdoc object for response data
XmlDocument ResponseDataXml = new XmlDocument();
ResponseDataXml.InnerXml= responsedata;
XmlNamespaceManager xnsm = new XmlNamespaceManager(ResponseDataXml.NameTable);
xnsm.AddNamespace("ps","http://example.com/namespace/ps");
//create xml document fro validating DTD
XmlDocument ValidateXml = new XmlDocument();
//select Nodes <ps:results> ... <result>
XmlNodeList NodesResults = ResponseDataXml.SelectNodes("ps:results/result");
foreach (XmlNode node in NodesResults)
{
ValidateXml.InnerText= "";
ValidateXml.InnerText += "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
ValidateXml.InnerText += "<!DOCTYPE SouborN1A SYSTEM \"validate_dtd.dtd\">";
ValidateXml.InnerText+=node.InnerXml;
ValidateXml.Save("validate_temp.xml");
if (validate("validate_temp.xml"))//validate() return true if document is valid
{
Console.WriteLine("Result:" + node.Attributes["id"] + " is valid !!!!!");
// here i can append "result" node in new xml document "Valid_result.xml"
}
else
{
Console.WriteLine("Result:"+ node.Attributes["id"] + "i invalid !!!!!");
// here i can append "result" in new xml document invalid result in to "invalid_result.xml"
}
}
Input postdata:
<ps:report xmlns:ps="example.com.....">
<results xmlns:ps="example.com....." ps:Identifikation="999999">
<result id="11125">
.......
</result>
<result id="1100">
.......
</result>
<result id="111999055">
.......
</result>
<result id="100000">
.......
</result>
</results>
</ps:report>
Please help me... :)
I do not know how to proceed, and work with a given output,
I need mainly validate the item separately and then store in a xml file.
I apologize for my English.
Thanks.
Unfortunately, this question is fairly broad, but generally speaking everything can be done with XML libraries in C#. For example, use the XmlDocument class to create elements and then simply append those elements to the existing nodes.
class Program
{
static void Main(string[] args)
{
var output = new XmlDocument();
output.AppendChild(output.CreateXmlDeclaration("1.0", "utf-8", null));
var xmlns = new XmlNamespaceManager(output.NameTable);
var root = output.CreateElement("ps", "report", "http://example.com/namespace/ps");
var list = output.CreateElement("ps", "results", "http://example.com/namespace/ps");
list.Attributes.Append(output.CreateAttribute("Identifikation"));
list.Attributes[0].Value = "999999";
root.AppendChild(list);
var item = output.CreateElement("ps", "result", "http://example.com/namespace/ps");
item.Attributes.Append(output.CreateAttribute("id"));
item.Attributes[0].Value = "11125";
list.AppendChild(item);
//TODO: Append more validation messages.
output.AppendChild(root);
output.WriteTo(new XmlTextWriter(Console.Out));
System.Console.ReadLine();
}
}
Produces a document like this:
<?xml version="1.0" encoding="utf-8"?>
<ps:report xmlns:ps="http://example.com/namespace/ps">
<ps:results Identifikation="999999">
<ps:result id="11125" />
</ps:results>
</ps:report>

Using Xpath With Default Namespace in C# for Canonicalisation

I'm trying to apply the C14N transform to some generated XML. It appears I can't use LINQ to retrieve the nodes to perform the canonicalisation so I have to go 'old school' with the DOM but I think I'm falling foul of the default namespace.
Here is a sample of my code.
static void Main(string[] args)
{
XmlDocument xDoc = new XmlDocument();
// Load some test xml
string path = #"..\..\TestFiles\Test_1.xml";
if (File.Exists(path) == true)
{
xDoc.PreserveWhitespace = true;
using (FileStream fs = new FileStream(path, FileMode.Open))
{
xDoc.Load(fs);
}
}
//Instantiate an XmlNamespaceManager object.
System.Xml.XmlNamespaceManager xmlnsManager = new System.Xml.XmlNamespaceManager(xDoc.NameTable);
//Add the namespaces used in books.xml to the XmlNamespaceManager.
xmlnsManager.AddNamespace("", "http://www.myApps.co.uk/");
// Create a list of nodes to have the Canonical treatment
//Execute the XPath query using the SelectNodes method of the XmlDocument.
//Supply the XmlNamespaceManager as the nsmgr parameter.
//The matching nodes will be returned as an XmlNodeList.
XmlNodeList nodeList = xDoc.SelectNodes("/ApplicationsBatch/Applications|/ApplicationsBatch/Applications//*", xmlnsManager);
// Perform the C14N transform on the data
XmlDsigC14NTransform transform = new XmlDsigC14NTransform();
transform.LoadInput(nodeList);
MemoryStream ms = (MemoryStream)transform.GetOutput(typeof(Stream));
File.WriteAllBytes(#"..\..\TestFiles\ModifiedTest_1", ms.ToArray());
}
And my XML:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<ApplicationsBatch xmlns="http://www.myApps.co.uk/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<MessageHeader>
<MessageID>00000003</MessageID>
<Body>11223344556</Body>
<Timestamp>2011-08-02T09:00:00</Timestamp>
<MessageCheck>?</MessageCheck>
</MessageHeader>
<Applications>
<Application>
<ApplicantDetails>
<Title>MR</Title>
<Forename>HOMER</Forename>
<Middlenames>
<Middlename></Middlename>
</Middlenames>
<PresentSurname>SIMPSON</PresentSurname>
<CurrentAddress>
<Address>
<AddressLine1>ADDRESS LINE1</AddressLine1>
<AddressLine2>ADDRESS LINE2</AddressLine2>
<AddressTown>ADDRESS Town</AddressTown>
<AddressCounty>COUNTY</AddressCounty>
<Postcode>POST CODE</Postcode>
<CountryCode>GB</CountryCode>
</Address>
<ResidentFromGyearMonth>2007-01</ResidentFromGyearMonth>
</CurrentAddress>
</ApplicantDetails>
</Application>
<Application>
<ApplicantDetails>
<Title>MR</Title>
<Forename>BART</Forename>
<Middlenames>
<Middlename></Middlename>
</Middlenames>
<PresentSurname>SIMPSON</PresentSurname>
<CurrentAddress>
<Address>
<AddressLine1>ADDRESS LINE1</AddressLine1>
<AddressLine2>ADDRESS LINE2</AddressLine2>
<AddressTown>ADDRESS Town</AddressTown>
<AddressCounty>COUNTY</AddressCounty>
<Postcode>POST CODE</Postcode>
<CountryCode>GB</CountryCode>
</Address>
<ResidentFromGyearMonth>2007-01</ResidentFromGyearMonth>
</CurrentAddress>
</ApplicantDetails>
</Application>
</Applications>
</ApplicationsBatch>
I've read a few other topics around the area and came across this Gem but it's not solved the problem.
Using the XPath Visualiser shows the required nodes should be selected but my code fails to select any.
I've found a partial answer to my problem.
When a new namespace is added to the manager it appears that the default namespace can't be an empty string.
This is what I ended up with:
//Instantiate an XmlNamespaceManager object.
System.Xml.XmlNamespaceManager xmlnsManager = new System.Xml.XmlNamespaceManager(xDoc.NameTable);
//Add the namespaces used to the XmlNamespaceManager.
xmlnsManager.AddNamespace("x", "http://www.myApps.co.uk/");
I then needed to modify the XPath to reflect the namespace identifier like this:
// Create a list of nodes to have the Canonical treatment
//Execute the XPath query using the SelectNodes method of the XmlDocument.
//Supply the XmlNamespaceManager as the nsmgr parameter.
//The matching nodes will be returned as an XmlNodeList.
XmlNodeList nodeList = xDoc.SelectNodes("/x:ApplicationsBatch/x:Applications|/x:ApplicationsBatch/x:Applications//*", xmlnsManager);
The nodes are now selected and ready for transformation... although that returns the correct structure of XML but all the values have been removed but that is a problem for another question.

Categories