Question

This sounds like a query that many would have but after hours of searching I can't even find a question addressing this problem.

I'm using LINQ2Xml classes to generate an XML document. I'm using a number of namespaces and the nodes are created dynamically so can't be declared using the nice new XElement("abc",new XElement...)) format.

Here's an example:

XNamespace ns1 = "scheme://ns1";
XNamespace ns2 = "scheme://ns2";
XNamespace ns3 = "scheme://ns3";
XNamespace ns4 = "scheme://ns4";

XElement el1 = new XElement(ns1 + "el1");
XElement el2 = new XElement(ns2 + "el2");
XElement el3 = new XElement(ns3 + "el3");
XElement el4 = new XElement(ns4 + "el4");

XElement el5 = new XElement(ns1 + "el5");
XElement el6 = new XElement(ns1 + "el5");
XElement el7 = new XElement(ns1 + "el5");

el4.Add(el7);
el3.Add(el6);
el2.Add(el5);

el1.Add(el2);
el1.Add(el3);
el1.Add(el4);

XDocument doc = new XDocument(el1);
Debug.Write(doc.ToString());

The above code produces the following output:

<el1 xmlns="scheme://ns1">
  <el2 xmlns="scheme://ns2">
    <el5 xmlns="scheme://ns1" />
  </el2>
  <el3 xmlns="scheme://ns3">
    <el5 xmlns="scheme://ns1" />
  </el3>
  <el4 xmlns="scheme://ns4">
    <el5 xmlns="scheme://ns1" />
  </el4>
</el1>

Whereas, I would like to have output more akin to the following (E&OE) where all namesapce declarations sit at the top and not on every element.

<el1 xmlns="scheme://ns1" xmlns:ns2="scheme://ns2" xmlns:ns3="scheme://ns3" xmlns:ns4="scheme://ns4">
    <ns2:el2 >
        <ns1:el5 />
    </ns2:el2>
    ....
</el1>

I have already tried the following which makes absolutely no difference:

IEnumerable<XAttribute> allNs = doc.Root.Descendants().Attributes().Where(a => a.IsNamespaceDeclaration);
int i = 0;
foreach (string uri in allNs.Select(a => a.Value).Distinct()) {
    string prefix = "ns" + ++i;
    doc.Root.Add(new XAttribute(XNamespace.Xmlns + prefix, uri));
}
Debug.Write(doc.ToString(SaveOptions.OmitDuplicateNamespaces));

Reasoning: I expect a file to have maybe up to a few dozen namespaces and several hundred elements from each. The uri for each namespace will be maybe 50 characters long as such, I will be saving thousands of characters by only declaring the namespaces once. Additionally, it just looks nicer!

Anyone know how this could be done?

Was it helpful?

Solution

Add other namespaces as attributes to the root element:

XElement el1 = new XElement(ns1 + "el1", 
    new XAttribute(XNamespace.Xmlns + "ns2", ns2),
    new XAttribute(XNamespace.Xmlns + "ns3", ns3),
    new XAttribute(XNamespace.Xmlns + "ns4", ns4));

Output:

<el1 xmlns:ns2="scheme://ns2" xmlns:ns3="scheme://ns3" xmlns:ns4="scheme://ns4" xmlns="scheme://ns1">
  <ns2:el2>
    <el5 />
  </ns2:el2>
  <ns3:el3>
    <el5 />
  </ns3:el3>
  <ns4:el4>
    <el5 />
  </ns4:el4>
</el1>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top