Managing XML namespace prefix scoping

S

ssamuel

Has anyone had any luck managing namespace scoping using
System.Xml.XmlDocument? Specifically, I'm trying to avoid this:

<root xmlns="urn:1">
<prefix:child xmlns:prefix="urn:2"/>
<prefix:child xmlns:prefix="urn:2"/>
</root>

I'd like to see something like this instead:

<root xmlns="urn:1" xmlns:prefix="urn:2">
<prefix:child/>
<prefix:child/>
</root>

I understand that if I'm using XML streams (like an XmlTextWriter)
it's impossible to predict all the prefixes at the root node, but if
I've got a DOM object (like an XmlDocument) I should be able to "up-
scope" prefix declarations one way or another.

I'm writing an API so I need a solid solution, not a hack.

Thanks,
Stephan
 
M

Martin Honnen

ssamuel said:
Has anyone had any luck managing namespace scoping using
System.Xml.XmlDocument? Specifically, I'm trying to avoid this:

<root xmlns="urn:1">
<prefix:child xmlns:prefix="urn:2"/>
<prefix:child xmlns:prefix="urn:2"/>
</root>

I'd like to see something like this instead:

<root xmlns="urn:1" xmlns:prefix="urn:2">
<prefix:child/>
<prefix:child/>
</root>

I understand that if I'm using XML streams (like an XmlTextWriter)
it's impossible to predict all the prefixes at the root node, but if
I've got a DOM object (like an XmlDocument) I should be able to "up-
scope" prefix declarations one way or another.

If needed you can create XML namespace declaration attributes on the
root element e.g. the following

XmlDocument xmlDoc = new XmlDocument();
XmlElement root =
(XmlElement)xmlDoc.AppendChild(xmlDoc.CreateElement("root", "urn:1"));
root.AppendChild(xmlDoc.CreateElement("prefix", "child", "urn:2"));
XmlAttribute ns1 = xmlDoc.CreateAttribute("xmlns", "prefix",
"http://www.w3.org/2000/xmlns/");
ns1.Value = "urn:2";
root.SetAttributeNode(ns1);
root.AppendChild(xmlDoc.CreateElement("prefix", "child", "urn:2"));
xmlDoc.Save(Console.Out);

serializes as

<root xmlns:prefix="urn:2" xmlns="urn:1">
<prefix:child />
<prefix:child />
</root>
 
S

ssamuel

Martin,

While that works for a brand new document, it doesn't work for one
that's already got nodes, namespaces and prefixes defined. When I try
to create the xmlns declaration on the document node of my existing
document, it tells me that my namespace declaration attribute has an
invalid namespaceURI.

My non-working code:

XmlNodeList nodesNamespaced = doc.SelectNodes("//
*[namespace-uri() != '']");
Dictionary<string, string> dictNS = new Dictionary<string,
string>();
foreach (XmlNode nodeNext in nodesNamespaced)
if (!dictNS.ContainsKey(nodeNext.Prefix))
dictNS.Add(nodeNext.Prefix,
nodeNext.NamespaceURI);
foreach (KeyValuePair<string, string> kvpNext in dictNS)
if (!String.IsNullOrEmpty(kvpNext.Key))

doc.DocumentElement.Attributes.Append(doc.CreateAttribute("xmlns",
kvpNext.Key, kvpNext.Value));

Is there a way to do this that's more namespace aware, i.e., not
trying to "figure out" the namespaces and "force" them into the
document element, but rather gently instruct the XmlDocument that it
should bubble declarations to the document element?

Thanks,

Stephan
 
M

Martin Honnen

ssamuel said:
While that works for a brand new document, it doesn't work for one
that's already got nodes, namespaces and prefixes defined. When I try
to create the xmlns declaration on the document node of my existing
document, it tells me that my namespace declaration attribute has an
invalid namespaceURI.

My non-working code:

XmlNodeList nodesNamespaced = doc.SelectNodes("//
*[namespace-uri() != '']");
Dictionary<string, string> dictNS = new Dictionary<string,
string>();
foreach (XmlNode nodeNext in nodesNamespaced)
if (!dictNS.ContainsKey(nodeNext.Prefix))
dictNS.Add(nodeNext.Prefix,
nodeNext.NamespaceURI);
foreach (KeyValuePair<string, string> kvpNext in dictNS)
if (!String.IsNullOrEmpty(kvpNext.Key))

doc.DocumentElement.Attributes.Append(doc.CreateAttribute("xmlns",
kvpNext.Key, kvpNext.Value));

The namespace of a namespace declaration attribute is predefined to be
http://www.w3.org/2000/xmlns/ so you need to pass that as the third
argument to CreateAttribute. Then you need to set the Value property to
kvpNext.Value, as I showed in my earlier sample

XmlAttribute ns1 = xmlDoc.CreateAttribute("xmlns", "prefix",
"http://www.w3.org/2000/xmlns/");
ns1.Value = "urn:2";
root.SetAttributeNode(ns1);

which then serializes as
Is there a way to do this that's more namespace aware, i.e., not
trying to "figure out" the namespaces and "force" them into the
document element, but rather gently instruct the XmlDocument that it
should bubble declarations to the document element?

I don't know of any such "instruction".
 
S

ssamuel

Martin,

Wow. That's a subtle but very important mistake I made. Thanks for the
solution and thanks for the catch. It's not exactly what I was looking
for, but it's worlds better than my next best contender, which
involved string manipulation.

Stephan
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top