views:

283

answers:

1

If a node belongs to a namespace, it's children by default belong to the same namespace. So there's no need to provide an xmlns attribute on each child, which is good.

However.

If I create two nodes like this:

Dim parent = <parent xmlns="http://my.namespace.org"/&gt;
Dim child = <child xmlns="http://my.namespace.org"&gt;value&lt;/child&gt;

parent.Add(child)

Console.WriteLine(parent.ToString)

The result is this:

<parent xmlns="http://my.namespace.org"&gt;
  <child xmlns="http://my.namespace.org"&gt;value&lt;/child&gt;
</parent>

But, if create them in a less convenient way:

Dim parent = <parent xmlns="http://my.namespace.org"/&gt;
Dim child As New XElement(XName.Get("child", "http://my.namespace.org")) With {.Value = "value"}

parent.Add(child)

Console.WriteLine(parent.ToString)

The result is more desirable:

<parent xmlns="http://my.namespace.org"&gt;
  <child>value</child>
</parent>

Obviously, I'd prefer to use the first way because it is so much more intuitive and easy to code. There's also another reason to not use method 2 -- sometimes I need to create nodes with XElement.Parse, parsing a string that contains an xmlns attribute, which produces exactly same results as method 1.

So the question is -- how do I get the pretty output of method 2, creating nodes as in method 1? The only option I see is to create a method that would clone given XElement, effectively recreating it according to method 2 pattern, but that seems ugly. I'm looking for a more obvious solution I overlooked for some reason.

+1  A: 

Ugh...namespaces - they will be the death of me.

Here you go:

Dim ns As XNamespace = "http://my.namespace.org"
Dim parent = <<%= ns + "parent" %>/>
Dim child = <<%= ns + "child" %>>value</>
parent.Add(child)

To be able to use XElement.Parse and keep child nodes in sync with their parent nodes' namespaces, it's best to use global namespaces. Really easy to do in VB.NET. At the top of your module/class, just use an Imports and all parents and children will use this namespace. For example:

Imports <xmlns="http://my.namespace.org"&gt;
Module Main
    Sub SomeSub()
        Dim child = <child>value</child>
        Dim parent = <parent><%= child %></parent>
        Console.WriteLine(parent.ToString)
    End Sub
End Module

Note that the <child/> element is created first. The same would apply to a non-default namespace, like Imports <xmlns:p="http://parent.namespace.org"&gt; and then creating with <p:child/> and <p:parent/>.

I once read, but have yet to find again, that mixing XML Literals with I-don't-know-what-you-call-it-but-it's-that-parent.Add(something)-thing is a bad idea.

Otaku
That's a great trick! A bit messy in the literals, but the output is cleaner.
Dennis Palmer
The trick is nice indeed, but it only addresses the problem partially. If I've got an XML string which I parse, the output contains namespaces for child nodes. So I was looking for a way to set some sort of I-don't-want-these flag against an already constructed node rather than a special way to construct a node -- because I cannot use that special way sometimes. Yet I'm happy to accept this answer if no other solution exists.
GSerg
@GSerg: Ah, I getcha now. I've updated the answer with what I hope will work for you.
Otaku