JesseJ

My application provides a GUI for users to edit xml documents. The resulting documents are used by other automated systems following a shared schema. I figured I could cut down on the amount of code I have to write if I used XSD.EXE to generate classes for me to databind the xml to my winforms controls. The nodes in the document are originally created in code using a default namespace declaration which is explicitly declared on the DocumentElement. To edit the nodes, I "deserialize" the OuterXml property of the target XmlNode into the appropriate class created by XSD.EXE, and the user can modify the properties with databound forms. When they click "Save", my code serializes the classes to xml in order to load them back into the source XmlDocument. That code looks like this:

Code Block

XmlSerializer serializer = new XmlSerializer(xsdObject.GetType());

StringWriter writer = new StringWriter();

serializer.Serialize(writer, xsdObject);

XmlDocument doc = new XmlDocument();

doc.LoadXml(writer.ToString());

// Retrieve the original XmlNode

XmlNode oldNode = this.getDefinitionNode(this.metaData.Definition);

oldNode.ParentNode.ReplaceChild(oldNode.OwnerDocument.ImportNode(doc.DocumentElement, true), oldNode);

It's ugly, but it works. My only problem is that the resulting xml comes with a bunch of extra xmlns attributes:

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:xsd="http://www.w3.org/2001/XMLSchema"

xmlns="http://superlongschemaurl"

I can delete the first two easily enough with no impact to schema validation, and I understand why I can't delete the last one (it's the namespace identifier that links to my schema). What I don't understand is why the resulting XmlNode that I import above explicitly writes the xmlns declaration to the file, even though the node doesn't have it before serialization, and since the default namespace declaration on the documentElement is in scope for all descendantelements.

When I create the original node in the application, I pass in the correct namespace for schema validation, but it's never explicitly written to the nodes in the document--the declaration appears explicitly only once on the documentElement and nowhere else. Isn't there some way I can associate the namespace with these imported nodes without it explicitly redeclaring it in the output XML I know it doesn't hurt anything, but there are literally thousands of nodes in the source document, all of which will be edited by the user, and these unnecessary declarations actually have a significant impact on the size of the file (which can reach into the megabytes all by itself). Any space savings is significant to performance in a lot of ways, so any input would be really helpful.

(Also, if anyone wants to suggest an easier way than using XSD classes to databind Xml to winforms controls, but that still benefits from schema constraints like XSD classes do, I'd definitely appreciate it.)

Thanks,

Jesse



Re: XML and the .NET Framework "Deserializing" XmlNode.OuterXml to object, then back to XmlNode questions

Martin Honnen

I am not sure I understand what the problem is. If you have a namespace declaration attribute on an element node then you can remove that, that might fix the problem with duplicated namespace declarations once you insert that element elsewhere. Here is a C# example:

Code Block

XmlDocument doc1 = new XmlDocument();

doc1.LoadXml("<root xmlns=\"http://example.com/ns1\"><bar/></root>");

XmlDocument doc2 = new XmlDocument();

doc2.LoadXml("<foo xmlns=\"http://example.com/ns1\"></foo>");

doc2.DocumentElement.RemoveAttribute("xmlns", "http://www.w3.org/2000/xmlns/");

doc1.DocumentElement.ReplaceChild(doc1.ImportNode(doc2.DocumentElement, true), doc1.DocumentElement.FirstChild);

doc1.Save(Console.Out);

Result is

http://example.com/ns1">
<foo />
</root>






Re: XML and the .NET Framework "Deserializing" XmlNode.OuterXml to object, then back to XmlNode questions

Martin Honnen

The result sample in the previous post is messed up, it should look like this:

Code Block

<root xmlns="http://example.com/ns1">
<foo />
</root>






Re: XML and the .NET Framework "Deserializing" XmlNode.OuterXml to object, then back to XmlNode questions

JesseJ

Ah, I'm extremely sorry, I forgot to mention that if I do strip off the xmlns attribute as you describe above, the element is indeed imported into the original document, but it loses its namespace and cannot be found in the default namespace upon further search using xpath/SelectSingleNode(). If I run XmlDocument.Validate(), I get a schema exception saying that the element wasn't expected in the default namespace. This appears to me to be some lame problem with the XmlNode in memory, because if I close and reopen the application (or load a new XmlDocument with old one's xml string contents), the imported element works perfectly.

In summary, I did not intend to mean only to imply that I *can't* remove the explicit namespace declaration (that is done quite easily), but that when I do, the importeded XmlNode behaves very badly in the original XmlDocument.

Any insights





Re: XML and the .NET Framework "Deserializing" XmlNode.OuterXml to object, then back to XmlNode questions

Martin Honnen

I am not able to reproduce the problem, both XPath and schema validation work normally for me, even when the xmlns attribute has been removed. The namespace of an element node in the DOM model is determined by its namespaceURI property and not by any namespace declaration attributes.

Here is my sample code:

Code Block

XmlDocument doc1 = new XmlDocument();

doc1.LoadXml("<root xmlns=\"http://example.com/ns1\"><bar/></root>");

XmlDocument doc2 = new XmlDocument();

doc2.LoadXml("<foo xmlns=\"http://example.com/ns1\"></foo>");

doc2.DocumentElement.RemoveAttribute("xmlns", "http://www.w3.org/2000/xmlns/");

doc1.DocumentElement.ReplaceChild(doc1.ImportNode(doc2.DocumentElement, true), doc1.DocumentElement.FirstChild);

doc1.Save(Console.Out);

Console.WriteLine();

XmlNamespaceManager mgr = new XmlNamespaceManager(doc1.NameTable);

mgr.AddNamespace("pf", "http://example.com/ns1");

Console.WriteLine("Number of nodes: {0}.", doc1.SelectNodes("//pf:foo", mgr).Count);

XmlSchemaSet schemaSet = new XmlSchemaSet();

schemaSet.Add(null, @"..\..\XMLSchema1.xsd");

doc1.Schemas = schemaSet;

doc1.Validate(delegate(object sender, ValidationEventArgs vargs)

{

Console.WriteLine("{0}: {1}", vargs.Severity, vargs.Message);

});

SelectNodes finds one node and schema validation outputs a validation error. I will post the schema sample in a follow up post as the forum software otherwise makes a mess of posted code samples.






Re: XML and the .NET Framework "Deserializing" XmlNode.OuterXml to object, then back to XmlNode questions

Martin Honnen

Here is the sample schema I have used:

Code Block

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://example.com/ns1"
elementFormDefault="qualified">
<xs:element name="root">
<xs:complexType>
<xs:choice>
<xs:element name="bar" type="xs:string" />
<!--
<xs:element name="foo" type="xs:string" />-->
</xs:choice>
</xs:complexType>
</xs:element>
</xs:schema>

This schema causes a validation error about the foo element. When I change the schema by uncommenting the foo element declaration then the Validate method does not report any validation errors. That all works as expected.

If you still have problems then try to post a minimal but complete sample that allows us the reproduce the problem.






Re: XML and the .NET Framework "Deserializing" XmlNode.OuterXml to object, then back to XmlNode questions

JesseJ

I wish there was a way I could write this where so I didn't have to incriminate myself, but I can't Wink I accidentally pasted the same constant for the RemoveAttribute URI argument that I use for my application schema, so I was using http://nottherightURI instead of http://www.w3.org/2000/xmlns/, which is why the xmlns attr wasn't being removed from the actual XML the normal way. I really got into trouble when I tried manually stripping xmlns before using LoadXML; that's why namespaceURI wasn't being populated on the resulting XmlNodes, which borked my source document when I imported the node (until, as I mentioned, the document was reloaded, at which time namespaceURI was repopulated for all nodes).

I'm an idiot, and I'm sorry I wasted your time, but thank you so much for taking the time to provide a code sample so I could see my problem, I really appreciate it!

JD