Adding a node to a document via XPath?

ikantspelwurdz

Well-known member
Joined
Dec 8, 2009
Messages
49
Programming Experience
1-3
Take this XML element:
VB.NET:
<root>
 <Alice>
  <HiredDate>1/1/2016</HiredDate>
 </Alice>
</root>

And this code - myXe is type "XElement":
VB.NET:
dim address as string = "/root/Alice/HiredDate"
dim value as string = "2/1/2016"

myXe.XPathSelectElement(address).Value = value

The first two lines simulate input from an external system. The rest of it is my own code. This works just fine, and does what I want it to do.

But now the external system is sending information about Bob, also using the XPath schema:
VB.NET:
dim address as string = "/root/Bob/HiredDate"
dim value as string = "2/11/2016"

myXe.XPathSelectElement(address).Value = value

Now my code crashes, because XPathSelectElement is returning Nothing.

I need the resulting xml to look like this:
VB.NET:
<root>
 <Alice>
  <HiredDate>2/1/2016</HiredDate>
 </Alice>
 <Bob>
  <HiredDate>2/11/2016</HiredDate>
 </Bob>
</root>

How can I do that? I'd rather not have to make an XPath walker. It is OK to assume that the XML will never contain repeating elements (e.g. root will never have more than one Alice, and Bob will never have more than one HiredDate, new values should simply overwrite the old ones). It's also OK to assume that incoming XPath strings will always take the form of "/root/node/nodes", though the number of subnodes won't always be two (it will always be at least one).
 
I went and added an XPath walker anyway.

VB.NET:
        Dim t() As String = address.Split({"/"}, System.StringSplitOptions.RemoveEmptyEntries)
        For i As Integer = 1 To t.Length - 1
            Dim nodeToCheck As String = ""
            Dim nodeToAdd As String = t(i)
            For j As Integer = 1 To i
                nodeToCheck &= "/"
                nodeToCheck &= t(j - 1)
            Next
            If myXe.XPathSelectElement(nodeToCheck).XPathSelectElement(nodeToAdd) Is Nothing Then
                myXe.XPathSelectElement(nodeToCheck).Add(New XElement(nodeToAdd))
            End If
        Next

        myXe.XPathSelectElement(address).Value = value

If there's something bad about this approach, I'd like to know.
 
You can do without Xpath queries here:
            Dim parts = address.Split("/".ToCharArray, StringSplitOptions.RemoveEmptyEntries).Skip(1).ToList
            Dim current = myXe
            parts.ForEach(Sub(name)
                              Dim child = current.Element(name)
                              If child Is Nothing Then
                                  child = New XElement(name)
                                  current.Add(child)
                              End If
                              current = child
                          End Sub)
            current.Value = value
 
It is a bit annoying that the Add method isn't a function that returns the element added. While it doesn't reduce the overall code you could refactor that to an extension method:
Public Module Extensions
    <Runtime.CompilerServices.Extension> Public Function AddChild(node As XElement, name As String) As XElement
        Dim child = New XElement(name)
        node.Add(child)
        Return child
    End Function
End Module

Then rewrite the previous code like this:
            Dim parts = address.Split("/".ToCharArray, StringSplitOptions.RemoveEmptyEntries).Skip(1).ToList
            Dim current = myXe
            parts.ForEach(Sub(name) current = If(current.Element(name), current.AddChild(name)))
            current.Value = value
 
Back
Top