2

I have to generate XML like the below:

<foo:document xmlns="http://www.example.com/xmlns" xmlns:foo="http://www.example.com/xmlns/foo-version1">
    <foo:bar foo:baz="true" />
</foo:document>

How can I generate this document with System.Xml.XmlDocument in c#?

dbc
  • 104,963
  • 20
  • 228
  • 340
Luciano Jeong
  • 325
  • 1
  • 10
  • Any particular reason you need `XmlDocument` rather than `XDocument`? LINQ to XML is *much* cleaner IMO in terms of namespace support. – Jon Skeet Oct 13 '19 at 14:52
  • Did you look at [How do I add multiple namespaces to the root element with XmlDocument?](https://stackoverflow.com/q/331568/3744182) and [How to add attributes to xml using XmlDocument in c# .net CF 3.5](https://stackoverflow.com/q/3405284/3744182) and [Creating an XML element with a namespace with XmlDocument.CreateElement()](https://stackoverflow.com/a/24689095/3744182)? Do those answer your question? If not, what have you tried so far, that did not work? – dbc Oct 13 '19 at 16:35
  • @dbc : Did you try creating the node? The default namespace is not the last namespace and Net library want to see the default as the last attribute. – jdweng Oct 13 '19 at 17:46
  • @jdweng - yes, I did. I was able to get the required output with both `XmlDocument` and `XDocument`. – dbc Oct 13 '19 at 19:50

2 Answers2

1

You can do this as follows:

var fooNs = "http://www.example.com/xmlns/foo-version1";
var defNs = "http://www.example.com/xmlns";

var doc = new XmlDocument();

// Create and add the root element
var root = doc.CreateElement("foo", "document", fooNs);
doc.AppendChild(root);

// Add the default namespace (do note the root element is not in this namespace)
var defAttr = doc.CreateAttribute("xmlns");
defAttr.Value = defNs;
root.Attributes.Append(defAttr);

// Create the <foo:bar> element
var bar = doc.CreateElement("foo", "bar", fooNs);
var bazAttr = doc.CreateAttribute("foo", "baz", fooNs);
bazAttr.Value = XmlConvert.ToString(true);
bar.Attributes.Append(bazAttr);

// Add it to the root
root.AppendChild(bar);

Notes:

  • When creating XmlElement and XmlAttribute nodes in a namespace, always prefer to use the Create() overloads that take a prefix, a localName and a namespaceURI:


    From a semantic point of view, what really matters is the node local name and namespace; the prefix is just a lookup to find a namespace declaration in scope.

  • Notice I didn't explicitly add the xmlns:foo="http://www.example.com/xmlns/foo-version1" attribute? It is unnecessary to do this since the root element was created using the required namespace and prefix via doc.CreateElement("foo", "document", fooNs). The framework (XmlWriter) will automatically emit the xmlns:foo attribute as it writes the XmlDocument to XML.

    If for some reason you need to explicitly create the namespace attribute, you can do it as follows:

    // The following is redundant as the framework (XmlWriter) will add the necessary
    // xmlns:foo attribute as the XmlDocument is being written.  If you need to do it anway 
    // (e.g. to control the order) you can do it as follows.
    // (But note that XML attributes are unordered according to the XML specification, for details
    // see https://stackoverflow.com/questions/33746224/in-xml-is-the-attribute-order-important)
    var xmlnsNs = "http://www.w3.org/2000/xmlns/";
    
    var fooAttr = doc.CreateAttribute("xmlns", "foo", xmlnsNs);
    fooAttr.Value = fooNs;
    root.Attributes.Append(fooAttr);
    

Demo fiddle #1 here.

Incidentally, as written in comments, it's much easier to do this with LINQ to XML:

XNamespace fooNs = "http://www.example.com/xmlns/foo-version1";
XNamespace defNs = "http://www.example.com/xmlns";

var root = new XElement(fooNs + "document"
                        // Add the namespace declarations with your desired prefixes.  Be sure to pass them into the constructor.
                        , new XAttribute("xmlns", defNs.ToString())
                        , new XAttribute(XNamespace.Xmlns + "foo", fooNs.ToString())
                        // And add any required content.  The content can be passed into the constructor, or added later. 
                        , new XElement(fooNs + "bar", new XAttribute(fooNs + "baz", true)));

Notes:

  • With LINQ to XML you never need to worry about the namespace prefix of an XElement or XAttribute. Just create them with the correct namespace and local name as encapsulated by XName. The framework (XmlWriter) will automatically emit all necessary namespace attributes as it writes.

    But if you do need to configure namespaces for some reason you can construct appropriate XAttribute objects and then pass them into the XElement constructor.

  • For further reading see How to: Create a Document with Namespaces (C#) (LINQ to XML).

Demo fiddle #2 here.

dbc
  • 104,963
  • 20
  • 228
  • 340
0

The Net library want the default namespace (without the prefix) to be the last namespace. I usually just use the Parse method which has less restrictions. See code below :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string xml = "<foo:document xmlns=\"http://www.example.com/xmlns\" xmlns:foo=\"http://www.example.com/xmlns/foo-version1\">" +
                         "   <foo:bar foo:baz=\"true\" />" +
                         "</foo:document>";

            XDocument doc = XDocument.Parse(xml);
        }
    }
}
jdweng
  • 33,250
  • 2
  • 15
  • 20