128

When generating XML from XmlDocument in .NET, a blank xmlns attribute appears the first time an element without an associated namespace is inserted; how can this be prevented?

Example:

XmlDocument xml = new XmlDocument();
xml.AppendChild(xml.CreateElement("root",
    "whatever:name-space-1.0"));
xml.DocumentElement.AppendChild(xml.CreateElement("loner"));
Console.WriteLine(xml.OuterXml);

Output:

<root xmlns="whatever:name-space-1.0"><loner xmlns="" /></root>

Desired Output:

<root xmlns="whatever:name-space-1.0"><loner /></root>

Is there a solution applicable to the XmlDocument code, not something that occurs after converting the document to a string with OuterXml?

My reasoning for doing this is to see if I can match the standard XML of a particular protocol using XmlDocument-generated XML. The blank xmlns attribute may not break or confuse a parser, but it's also not present in any usage that I've seen of this protocol.

pnuts
  • 58,317
  • 11
  • 87
  • 139
Neil C. Obremski
  • 18,696
  • 24
  • 83
  • 112

7 Answers7

117

Thanks to Jeremy Lew's answer and a bit more playing around, I figured out how to remove blank xmlns attributes: pass in the root node's namespace when creating any child node you want not to have a prefix on. Using a namespace without a prefix at the root means that you need to use that same namespace on child elements for them to also not have prefixes.

Fixed Code:

XmlDocument xml = new XmlDocument();
xml.AppendChild(xml.CreateElement("root", "whatever:name-space-1.0"));
xml.DocumentElement.AppendChild(xml.CreateElement("loner", "whatever:name-space-1.0")); 
Console.WriteLine(xml.OuterXml);

Thanks everyone to all your answers which led me in the right direction!

infojolt
  • 5,244
  • 3
  • 40
  • 82
Neil C. Obremski
  • 18,696
  • 24
  • 83
  • 112
  • 1
    Precisely. Putting the element in the "whatever:name-space-1.0" namespace means that the empty xmlns attribute (which puts it in no namespace) won't be added to it when it's serialised. If you need refreshing on how namespaces work, have a look at http://www.jclark.com/xml/xmlns.htm – JeniT Sep 26 '08 at 08:25
  • 3
    Watch out: Elements need this (or the perhaps better `doc.DocumentElement.NamespaceURI`) but don't specify a namespace for `CreateAttribute()` of you'll get the `xmlns:psomething` even if it is the same uri. – Jason Kleban Jul 15 '15 at 00:28
98

This is a variant of JeniT's answer (Thank you very very much btw!)

XmlElement new_element = doc.CreateElement("Foo", doc.DocumentElement.NamespaceURI);

This eliminates having to copy or repeat the namespace everywhere.

C.J.
  • 15,637
  • 9
  • 61
  • 77
  • 5
    Best response according to me. We don't have to know what is document default namespace (usefull when we don't create a xml file from scratch ie in read and modify scenarios). – MuiBienCarlota Oct 04 '12 at 09:12
12

If the <loner> element in your sample XML didn't have the xmlns default namespace declaration on it, then it would be in the whatever:name-space-1.0 namespace rather than being in no namespace. If that's what you want, you need to create the element in that namespace:

xml.CreateElement("loner", "whatever:name-space-1.0")

If you want the <loner> element to be in no namespace, then the XML that's been produced is exactly what you need, and you shouldn't worry about the xmlns attribute that's been added automatically for you.

JeniT
  • 3,660
  • 20
  • 11
  • 3
    The problem lies with non-compliant XML parsers (typically from Microsoft) that can't cope with xmnls=""). – Craig Trader Sep 25 '08 at 18:27
  • 2
    /. called. They want their random MS bashing comment back. –  Sep 25 '08 at 18:32
  • @W. Craig Trader - can't say I've encountered that as a problem. Example? – Kev Sep 25 '08 at 18:48
  • 1
    Correct, I do not want the node to have a namespace, but I don't want it to have a blank namespace attribute (xmlns) either. My reasoning is just to see if I can match the XML output of a particular protocol which is setup like this. – Neil C. Obremski Sep 25 '08 at 19:26
  • 5
    It wasn't random bashing. The Microsoft Updater Application Block uses an XML Manifest to determine what to deliver to a client. Unfortunately, the Manifest parser can't cope with xmlns=""; I had to write a post-processor that would strip out the empty xmlns attributes. – Craig Trader Sep 25 '08 at 20:39
7

Since root is in an unprefixed namespace, any child of root that wants to be un-namespaced has to be output like your example. The solution would be to prefix the root element like so:

<w:root xmlns:w="whatever:name-space-1.0">
   <loner/>
</w:root>

code:

XmlDocument doc = new XmlDocument();
XmlElement root = doc.CreateElement( "w", "root", "whatever:name-space-1.0" );
doc.AppendChild( root );
root.AppendChild( doc.CreateElement( "loner" ) );
Console.WriteLine(doc.OuterXml);
jlew
  • 10,491
  • 1
  • 35
  • 58
0

If possible, create a serialization class then do:

XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
XmlSerializer serializer = new XmlSerializer(yourType);
serializer.Serialize(xmlTextWriter, someObject, ns);

It's safer, and you can control the namespaces with attributes if you really need more control.

ilitirit
  • 16,016
  • 18
  • 72
  • 111
0

Yes you can prevent the XMLNS from the XmlElement . First Creating time it is coming : like that

<trkpt lat="30.53597" lon="-97.753324" xmlns="">
    <ele>249.118774</ele>
    <time>2006-05-05T14:34:44Z</time>
</trkpt>

Change the code : And pass xml namespace like this

C# code:

XmlElement bookElement = xdoc.CreateElement("trkpt", "http://www.topografix.com/GPX/1/1");
bookElement.SetAttribute("lat", "30.53597");
bookElement.SetAttribute("lon", "97.753324");
John Saunders
  • 160,644
  • 26
  • 247
  • 397
0

I've solved the problem by using the Factory Pattern. I created a factory for XElement objects. As parameter for the instantiation of the factory I've specified a XNamespace object. So, everytime a XElement is created by the factory the namespace will be added automatically. Here is the code of the factory:

internal class XElementFactory
{
    private readonly XNamespace currentNs;

    public XElementFactory(XNamespace ns)
    {
        this.currentNs = ns;
    }

    internal XElement CreateXElement(String name, params object[] content)
    {
        return new XElement(currentNs + name, content);
    }
}
brinke
  • 9
  • 1