XMLNode.Clone bug or feature?

I’ve been stumped for so long by unexpected behavior from the XMLNode.Clone function. The problem is after I clone a node I am unable to read attribute values from the clone. I can “set” new values for attributes in the clone, but can’t “get” them.

I’ve modified the Xojo example file “XMLExample.xojo_binary_project” that’s included with Xojo to demonstrate the problem. I’ve add a method named “CloneNode” to the example project that has a couple of break points in it to help illustrate what I’m seeing. The listbox output of the example project is unimportant so just ignore. Here’s the code for the “CloneNode” method or you can download the modified example project from this link and run it instead:

https://edittoolsspace.nyc3.digitaloceanspaces.com/Xojo/XMLExample_CLONE_TEST.xojo_binary_project

Please have a look when you can. This one’s driving me crazy. All help appreciated.

Var n As XmlNode = xml.DocumentElement.FirstChild

If n <> Nil Then
  // Clone the first team (Seagulls)
  Var dup As XmlNode = n.Clone(True)
  
  // Change the cloned node's name attribute to "Eagles"
  dup.SetAttribute("name", "Eagles")
  
  // Add it to the XmlDocument
  n.Parent.AppendChild(dup)
End If

//Find the new Eagles team node which is a clone of the Seagulls team node
for i as integer = 0 to xml.DocumentElement.ChildCount - 1
  dim team as XmlNode =  xml.DocumentElement.Child(i)
  if team.GetAttribute("name") = "Eagles" then
    //Found the Eagles team node which is the clone
    //Now try to read its attributes
    dim playerName as string = team.FirstChild.GetAttribute("name") 'get the value of the "name" attribute of the first player
    dim playerPosition as string = team.FirstChild.GetAttribute("position") 'get the value of the "position" attribute of the first player
    break 'result = cannot read the attributes from the cloned "Seagulls" node
  end if
next i

//Using the same code, try reading the attributes of the original "Seagulls" node
for i as integer = 0 to xml.DocumentElement.ChildCount - 1
  dim team as XmlNode =  xml.DocumentElement.Child(i)
  if team.GetAttribute("name") = "Seagulls" then
    //Found the Seagulls team node
    //Now try to read its attributes
    dim playerName as string = team.FirstChild.GetAttribute("name") 'get the value of the "name" attribute of the first player
    dim playerPosition as string = team.FirstChild.GetAttribute("position") 'get the value of the "position" attribute of the first player
    break 'result = successfully reads the attributes from the original "Seagulls" node
  end if
next i

I don’t have time to do any more, but the attribute count is coming out different. If you do this after the clone (even before you rename it to Eagles):

System.DebugLog Str(n.FirstChild.AttributeCount)
System.DebugLog Str(dup.FirstChild.AttributeCount)

you get 2 in the original vs 1 in the clone. I need to step away, but maybe that’s something to look at.

I see what you mean, Bill. It seems like the clone’s structure isn’t getting fully populated. As a side note, If you “set” the attribute before you “get” the same attribute, you can read it as expected. I added the code below at line 21 and it works normally:

team.FirstChild.SetAttribute("name", "test")

I’ve been beating my head against the wall trying to get the clone method to work. Unless I’m missing something, it looks like a bug. Is there workaround? I’ve tried dimming a new XMLNode var and assigning the node you want to clone to it (theCloneNode = theOrignalNode), but have gotten some strange results.

It’s interesting the node.toString displays all the clone’s attributes correctly, you just can’t “get” them.

There are definitely weird things going on. Add this in the loop after you Dim team, before checking for “Eagles”:

For j As Integer = 0 To team.firstChild.AttributeCount - 1
  System.DebugLog team.FirstChild.GetAttributeNode(j).Name
Next j

You can see there is an extra attribute in the cloned one, “xmlns:xml”. I’m not sure what it’s doing there.

@Bill_Gookin
I’m going to submit a bug report on this. This is a showstopper for my current project. Any ideas for a workaround?

I use a custom GetAttrValue routine that also has a default if the attribute doesn’t exist. I return the default value if the attribute is there but is an empty string, you may not want that. Note that this needs to be in a module. I tested and it does work for your example cloned node.

Public Function GetAttrValue(extends xml as xmlNode, attributeName as string, defaultValue as string = "", isCaseSensitive as Boolean = False) as String
  Dim co As ComparisonOptions = ComparisonOptions.CaseInsensitive
  If isCaseSensitive Then co = ComparisonOptions.CaseSensitive
  
  Dim i As Integer
  For i = 0 To xml.AttributeCount - 1
    If xml.GetAttributeNode(i).Name.Compare(attributeName, co) = 0 Then
      //Note that I have this set to return the default value if an empty attribute is found
      //You may want to actually return the empty string instead
      If xml.GetAttributeNode(i).Value <> "" Then
        Return xml.GetAttributeNode(i).Value
      Else
        Return defaultValue
      End If
    End If
  Next
  
  //If we get here, it wasn't found so return the default
  Return defaultValue
  
  
  
End Function

@Bill_Gookin
Thanks, Bob. I just plugged the code into the sample project and it worked nicely. I’ll try it in the app I’m developing tonight and let you know how it goes.
Many thanks!

@Bill_Gookin
The code works perfectly in my app in development as well. It’s funny how other issues also clear up once there’s a workaround for a problem. Thanks, so much, for our help.

I did file a bug report on this. #61349

Awesome, I’m glad it works. I wish I knew why the clone function wasn’t working. Interestingly, when I tried to write my own clone routine (getting the ToString from the original node, creating a new node from that string, and then importing the node), it ended up with the same problem as the clone routine.

I guess you could create your own clone routine looping through attributes and children, but I like having a default attribute value anyway in case it’s undefined, so my routine works for me. :slight_smile:

I think this issue might be related to this one;
<https://xojo.com/issue/24322>

‘Using XMLDocument.ImportNode prevents XMLNode.GetAttribute from working’

1 Like

@Paul_Levine
Thanks, Paul. I did search the bug list to see if there were any earlier reports of problems with XMLNode.Clone but nothing came up. I think the search function only looks at the report titles, so I missed yours.

I find it distressing/disappointing that you reported this bug 7.5 years ago and it hasn’t been fixed. There’s obviously a serious problem in the XML functions that Xojo needs to address.

1 Like

Yeah when I pulled up the bug report I was shocked that it’s been 7 years. I remember struggling through the same issue you had.