XElement.Load() and "undeclared prefix" exception - c#

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

Related

XML Soap Get Element From Xelement

the xml that i get via a response stream:
<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"xmlns:xsd="http://www.w3.org/2001/XMLSchema"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<generateSSOResponse xmlns="http://url.com">
<generateSSOReturn>2DKtjZNq58THggh42lNsGvgGTjF8RSBA</generateSSOReturn>
</generateSSOResponse>
</soapenv:Body>
</soapenv:Envelope>
The code is use to try and get the "generateSSOResponse" token value.
var xmlDoc = XElement.Parse(s);
var ssoToken = xmlDoc.XPathSelectElement("/soapenv:Envelope[#xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"]/soapenv:Body/generateSSOResponse[#xmlns=\"http://ws.configureone.com\"]/generateSSOReturn");
Error: Namespace Manager or XsltContext needed. This query has a
prefix, variable, or user-defined function.
Saying i need a namespace manager? i though that's when dealing with XMLdoc not xElement? Whats the solution here?
EDIT: variable "s" is the response stream code as :
using (var mem = new MemoryStream())
{
rstream.CopyTo(mem);
var b = mem.ToArray();
var s = System.Text.Encoding.UTF8.GetString(b);
Honestly, it'd be far simpler to use LINQ to XML as it was intended:
XNamespace ns = "http://url.com";
var token = (string)doc.Descendants(ns + "generateSSOReturn").Single();
See this fiddle for a working example. If you did want to use XPath then yes, you would need a namespace manager to allow the XPath navigator to resolve all the prefixes in your expression.
As an aside, you could also parse your XML direct from the stream:
var doc = XDocument.Load(rstream);
Ok so Charles Mager gave an answer using XMLtoLINQ as I was trying to use Xelement. However it turns out the ERP the code is being embedded into doesn't support linq (bummer).
So here's the solution i got working without XMLtoLINQ:
XmlDocument mydoc = new XmlDocument();
XmlNamespaceManager manager = new XmlNamespaceManager(mydoc.NameTable);
manager.AddNamespace("soapenv", "http://schemas.xmlsoap.org/soap/envelope/");
manager.AddNamespace("xsd", "http://www.w3.org/2001/XMLSchema");
manager.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
manager.AddNamespace("rsp","http://url.com");
mydoc.LoadXml(s);
var mytoken = mydoc.SelectSingleNode("//rsp:generateSSOReturn", manager);
Hope this helps anyone else who is in the same predicament as I was.

Deserialize XML Fragment with Namespace using C#

I'm having issues deserializing the following XML fragment (from OneNote):
<one:OE creationTime="2015-03-21T18:32:38.000Z" lastModifiedTime="2015-03-21T18:32:38.000Z" objectID="{649CA68C-C596-4F89-9885-1553A953529E}{30}{B0}" alignment="left" quickStyleIndex="1" selected="partial">
<one:List>
<one:Bullet bullet="2" fontSize="11.0" />
</one:List>
<one:T><![CDATA[Bullet point one]]></one:T>
</one:OE>
The following code is used to deserialize the above fragment. The OE class has the following attributes:
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.34230")]
[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://schemas.microsoft.com/office/onenote/2013/onenote")]
[System.Xml.Serialization.XmlRootAttribute("OE", Namespace = "http://schemas.microsoft.com/office/onenote/2013/onenote", IsNullable = true)]
public partial class OE : EntityBase<OE>
{
...
}
And the actual method to deserialize the fragment is in the base class, EntityBase:
public static T Deserialize(string xml)
{
System.IO.StringReader stringReader = null;
try
{
stringReader = new System.IO.StringReader(xml);
return ((T)(Serializer.Deserialize(System.Xml.XmlReader.Create(stringReader))));
}
finally
{
if ((stringReader != null))
{
stringReader.Dispose();
}
}
}
The deserialize method is called as follows:
var element = OE.Deserialize(xmlString);
Where the variable xmlString is the XML fragment given above. On calling the Deserialize method, I get the following error:
There is an error in XML document (1,2). ---> System.Xml.XmlException: 'one' is an undeclared prefix. Line 1, position 2.
I have spent some time looking at the attributes declaring the namepaces in the OE class, but everything appears to be correct. Can anyone point out to the mistake I'm making?
The answer given by matrixanomaly is correct, but unfortunately, the OneNote namespace given is incorrect. I'm working with OneNote 2013 and not 2010. The actual code I used to deserialize the same XML fragment as given in my question is as follows:
public static OE DeserializeFragment(string xmlFragment)
{
var serializer = new System.Xml.Serialization.XmlSerializer(typeof(OE));
System.IO.StringReader stringReader = null;
try
{
stringReader = new System.IO.StringReader(xmlFragment);
NameTable nt = new NameTable();
XmlNamespaceManager nsManager = new XmlNamespaceManager(nt);
nsManager.AddNamespace("one", "http://schemas.microsoft.com/office/onenote/2013/onenote");
XmlParserContext context = new XmlParserContext(null, nsManager, null, XmlSpace.None);
XmlReaderSettings xmlReaderSettings = new XmlReaderSettings();
xmlReaderSettings.ConformanceLevel = ConformanceLevel.Fragment;
return ((OE)(serializer.Deserialize(System.Xml.XmlReader.Create(stringReader, xmlReaderSettings, context))));
}
finally
{
if ((stringReader != null))
{
stringReader.Dispose();
}
}
}
I think you need the original namespace declaration for one. This is because one is a namespace and items like OE and List are prefixes, which exist in the namespace created by oneNote, which the declaration isn't present in the fragment you posted. A prefix exists to avoid collisions in naming in the event that different XML documents get mixed together. see a w3schools link for further explanation
So a workaround would be to append the namespace such as <xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote"> to each fragment (doesn't seem to be the most optimal, but eh it works), and go about deserializing it as you've done.
I don't have OneNote handy so that namespace declaration was from this forum post.
An alternate way of deserializing XML fragments is through XMLReader and XMLReaderSettings, where you can set the coformance level to Fragment. And adding a pre-defined namespace.
Example adapted from this MSDN blog
XmlDocument doc = new XmlDocument();
NameTable nt = new NameTable();
XmlNamespaceManager nsmgr = new XmlNamespaceManager(nt);
nsmgr.AddNamespace("one", "http://schemas.microsoft.com/office/onenote/2010/onenote");
XmlParserContext context = new XmlParserContext(null, nsmgr, null, XmlSpace.None);
XmlReaderSettings xset = new XmlReaderSettings();
xset.ConformanceLevel = ConformanceLevel.Fragment;
XmlReader rd = XmlReader.Create(new StringReader(XMLSource), xset, context);
doc.Load(rd);
I personally prefer using XMLReader and XMLReader settings, though they seem like more work having to CreateReader() and set things up, it looks to be a more robust way.
Or, if you don't want to deal with custom namespaces and what not, and don't run into the problem of collisions, just programatically remove the front declaration of one:, and move on with deserializing it. That's more of string manipulation, though.

XML Serializer C# Namespaces Duplicate Issue

I am using XmlSerializer in C# to generate an XML document based on a model. I need to generate the following XML root that contains a duplicate namespace using separate prefixes. Below is what the output should look likeā€¦
<ClinicalDocument xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:hl7-org:v3 CDA_SDTC.xsd"
xmlns="urn:hl7-org:v3"
xmlns:cda="urn:hl7-org:v3"
xmlns:sdtc="urn:hl7-org:sdtc">
However, when I Serialize this, the default entry is removed (which contains the duplicate namespace) and the root is prefixed.
<cda:ClinicalDocument xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:sdtc="urn:hl7-org:sdtc" xsi:schemaLocation="http://www.w3.org/2001/XMLSchema-instance" xmlns:cda="urn:hl7-org:v3">
Here is my XmlSerializer code...
var writer = new XmlSerializer(clinicalDocument.GetType(),"urn:hl7-org:v3");
var myNamespace = new XmlSerializerNamespaces();
myNamespace.Add("sdtc", "urn:hl7-org:sdtc");
myNamespace.Add("xsi", "http://www.w3.org/2001/XMLSchema-instance");
myNamespace.Add("cda", "urn:hl7-org:v3");
using (var file = new System.IO.StreamWriter(CCDUncOutputPath))
{
writer.Serialize(file, clinicalDocument, myNamespace);
file.Close();
};
writer = null;
GC.Collect();
Does anyone have a fix for this?

"Root element is missing" exception given when trying to parse XML file

I'm trying to set up parsing for a test XML generated with ksoap2 in Android:
<?xml version="1.0" encoding="utf-8"?>
<v:Envelope xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://www.w3.org/2001/XMLSchema" xmlns:c="http://schemas.xmlsoap.org/soap/encoding/" xmlns:v="http://schemas.xmlsoap.org/soap/envelope/">
<v:Header />
<v:Body>
<v:SOAPBODY>
<v:INFO i:type="v:INFO">
<v:LAITETUNNUS i:type="d:string">EI_TUNNUSTA</v:LAITETUNNUS>
</v:INFO>
<v:TOIMINNOT i:type="v:TOIMINNOT">
<v:TOIMINTA i:type="d:string">ASETUKSET_HAKU</v:TOIMINTA>
</v:TOIMINNOT>
<v:SISALTO i:type="v:SISALTO">
<v:KUVA i:type="d:string">AGFAFDGFDGFG</v:KUVA>
<v:MITTAUS i:type="d:string">12,42,12,4,53,12</v:MITTAUS>
</v:SISALTO>
</v:SOAPBODY>
</v:Body>
</v:Envelope>
But seemingly i can't parse it in any way. The exception is always that "Root element is not found" even when it goes through XML-validators like the one at w3schools. If i'm correct the contents of the body shouldn't be an issue when the problem is with root element.
The test code for parsing i try to use in C# is:
using (StreamReader streamreader = new StreamReader(Context.Request.InputStream))
{
try
{
XDocument xmlInput = new XDocument();
streamreader.BaseStream.Position = 0;
string tmp = streamreader.ReadToEnd();
var xmlreader = XmlReader.Create(streamreader.BaseStream);
xmlInput = XDocument.Parse(tmp);
xmlInput = XDocument.Load(xmlreader);
catch (Exception e)
{ }
where the xmlInput = XDocument.Parse(tmp); does indeed parse it to a XDocument, not a navigable one, though. Then xmlInput = XDocument.Load(xmlreader); throws the exception for not having a root element. I'm completely at loss here because i managed to parse and navigate the almost same xml with XMLDocument and XDocument classes before, and i fear i made some changes i didn't notice.
Thanks in advance.
Update: Here's the string tmp as requested :
"<?xml version=\"1.0\" encoding=\"utf-8\"?><v:Envelope xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:d=\"http://www.w3.org/2001/XMLSchema\" xmlns:c=\"http://schemas.xmlsoap.org/soap/encoding/\" xmlns:v=\"http://schemas.xmlsoap.org/soap/envelope/\"><v:Header /><v:Body><v:SOAPBODY><v:INFO i:type=\"v:INFO\"><v:LAITETUNNUS i:type=\"d:string\">EI_TUNNUSTA</v:LAITETUNNUS></v:INFO><v:TOIMINNOT i:type=\"v:TOIMINNOT\"><v:TOIMINTA i:type=\"d:string\">ASETUKSET_HAKU</v:TOIMINTA></v:TOIMINNOT><v:SISALTO i:type=\"v:SISALTO\"><v:KUVA i:type=\"d:string\">AGFAFDGFDGFG</v:KUVA><v:MITTAUS i:type=\"d:string\">12,42,12,4,53,12</v:MITTAUS></v:SISALTO></v:SOAPBODY></v:Body></v:Envelope>\r\n"
Update: Even with XDocument.Load(new StreamReader(Context.Request.InputStream, Encoding.UTF8)); the parsing will fail.
I believe you've read to the end of the stream once already, you need to reset the position in the stream again. see: "Root element is missing" error but I have a root element

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