Reputation: 139
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
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