Search code examples
xmlnamespaceslinq-to-xmlxelement

How do I compose XDocument and keep the same namespace prefix throughout?


I'm having trouble composing an XDocument which uses two namespaces. When I add XElements created by a different method (which refer to the exact same XNamespace instances), I get a redeclaration of the xmlns with a different prefix. It's perfectly correct XML, but is a bear for human readability.

XDocument xml = new XDocument();
XElement e_graphml = new XElement(ns_graphML + "graphml",
            new XAttribute("xmlns", ns_graphML),
            new XAttribute(XNamespace.Xmlns + "y", ns_yGraphML));
xml.Add(e_graphml);
XElement child = graph.ToX();
e_graphml.Add(child);

The graph object uses my globally available ns_graphML and ns_yGraphML objects, both type XNamespace. Yet the XML I get back serializes to text as:

<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:y="http://www.yworks.com/xml/graphml">
  <graph p3:edgedefault="directed" p3:id="fileReferences" xmlns:p3="http://graphml.graphdrawing.org/xmlns" />
</graphml>

(EDIT) I expect:

<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:y="http://www.yworks.com/xml/graphml">
  <graph edgedefault="directed" id="fileReferences"/>
</graphml>

(/EDIT)

The graph element should inherit the default xmlns once it is added to e_graphml, but apparently these are considered different. Note thate graph.ToX() does not add explicit namespace attributes (xmlns=...) to the returned graph XElement; the XNames in it simply refer to the namespace, like so:

XElement e_graph = new XElement(ns_graphML + "graph",
    new XAttribute(ns_graphML + "edgedefault", "directed"),
    new XAttribute(ns_graphML + "id", Name));

Perhaps this is a duplicate of Force XDocument to not use namespace prefix if namespace is also defined as default, but I'm creating the XDocument entirely in code, not from initial XML text.


Solution

  • I think this behavior is intended. Attributes without a namespace prefix isn't part of any namespace, not even the default namespace. It needed to put the attribute in that namespace but since it didn't have a prefix to use, it had to create one. I think it'll be easier to just create the document but use explicit prefixes for the namespaces, it'll come out a lot cleaner.

    var e_graphml = new XElement(ns_graphML + "graphml",
        new XAttribute(XNamespace.Xmlns + "g", ns_graphML),
        new XAttribute(XNamespace.Xmlns + "y", ns_yGraphML)
    );
    

    This will yield xml like so:

    <g:graphml xmlns:g="http://graphml.graphdrawing.org/xmlns" xmlns:y="http://www.yworks.com/xml/graphml">
        <g:graph g:edgedefault="directed" g:id="fileReferences" />
    </g:graphml>
    

    If you specifically want to have it render the attributes without prefixes, remove the namespace when you generate them. Attributes typically don't need to be namespaced unless explicitly required.

    var e_graph = new XElement(ns_graphML + "graph",
        new XAttribute("edgedefault", "directed"),
        new XAttribute("id", Name)
    );
    
    <graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:y="http://www.yworks.com/xml/graphml">
        <graph edgedefault="directed" id="fileReferences" />
    </graphml>