Marcus Guinane
Marcus Guinane

Reputation: 139

Adding namespace attribute to XElement - how to prevent blank/empty namespace on child elements?

I need to read an xml document from a database record into an XDocument object in order for it to be deserialized. So that the deserialization will work, I need to apply a specific namespace to each of the level 1 elements. So XML looks a bit like this:

<Itinerary xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <Segments>
        <SegmentFlight>
        </SegmentFlight>
        <!-- more child elements -->
    </Segments>
    <References>
        <!-- child elements -->
    </References>
    <Fares>
        <!-- child elements -->
    </Fares>
</Itinerary>

And I need it to look like this:

<Itinerary xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <Segments xmlns="http://myurl">
        <SegmentFlight>
        </SegmentFlight>
        <!-- more child elements -->
    </Segments>
    <References xmlns="http://myurl">
        <!-- child elements -->
    </References>
    <Fares xmlns="http://myurl">
        <!-- child elements -->
    </Fares>
</Itinerary>

But when I run the following code to apply the namespace to each of the top-level elements within the Itinerary node:

Dim xmlDoc As XDocument = XDocument.Load(New System.IO.StringReader(xmlStringFromDB))
Dim ns As XNamespace = "http://myurl"

For Each elem In xmlDoc.Descendants("Itinerary").Elements
    elem.Name = ns + elem.Name.LocalName
Next    

I get a blank xmln="" namespace attribute on each child element within that element, which causes the deserialization to fail:

<Itinerary xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <Segments xmlns="http://myurl">
        <SegmentFlight xmlns="">
            <!-- etc ... -->
        </Segments>

How do I prevent the blank/empty namespace being added to each child element of the element to which the required namespace has been applied?

Upvotes: 3

Views: 3234

Answers (1)

Chris Haas
Chris Haas

Reputation: 55417

Remove the Elements from your For loop, it was causing all child elements to be processed, too.

    For Each elem In xmlDoc.Descendants("Itinerary") ''//.Elements
        elem.Name = ns + elem.Name.LocalName
    Next

EDIT

Sorry, that didn't work as you noticed, I hadn't had my coffee yet.

The reason that .Net is doing that is because you are resetting the default namespace in the middle of a document. If it didn't append the empty namespace to the child elements then all child elements of <Segments> would be automatically part of the http://myurl namespace. Maybe this is the result that you want but since you didn't tell .Net that its assuming you don't.

To say that in a different way, the output that you are getting says that <Itinerary> is in the empty namespace, <Segments> is in the http://myurl namespace and <SegmentFlight> is in the same empty namespace as <Itinerary>. If you want <SegmentFlight> to be part of the same namespace as <Segments> then you need to recursively apply the namespace. When you call ToString() .Net will output what you are expecting. Here's a recursive version:

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    Dim xmlDoc As XDocument = XDocument.Load(New System.IO.StringReader(xmlStringFromDB))

    Dim ns As XNamespace = "http://myurl"
    ApplyNameSpaceToAllChildren(xmlDoc.Descendants("Itinerary").Elements(), ns)

    Trace.WriteLine(xmlDoc.ToString())
End Sub
Private Sub ApplyNameSpaceToAllChildren(ByVal elements As IEnumerable(Of XElement), ByVal ns As XNamespace)
    For Each elem In elements
        elem.Name = ns + elem.Name.LocalName
        If elem.HasElements Then
            ApplyNameSpaceToAllChildren(elem.Elements, ns)
        End If
    Next
End Sub

This outputs:

<Itinerary xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <Segments xmlns="http://myurl">
    <SegmentFlight></SegmentFlight>
    <!-- more child elements -->
  </Segments>
  <References xmlns="http://myurl">
    <!-- child elements -->
  </References>
  <Fares xmlns="http://myurl">
    <!-- child elements -->
  </Fares>
</Itinerary>

Upvotes: 1

Related Questions