2

I have a XML document like this

<Names>
    <abc>john</abc>
    <abc>Ram</abc>
</Names>

Now I want change the tag name "abc" to "name" i.e.

<Names>
    <name>john</name>
    <name>Ram</name>
</Names>

Can anyone tell me how to do this using Tcl script with tdom library or any other way to do this?

user2940110
  • 69
  • 2
  • 7

1 Answers1

3

Here is one way to do it:

  1. Get a list of <abc> nodes,
  2. For each <abc> node, create a new <name> node, copy over the text inside
  3. Replace the old node with new one

Here is the code:

package require tdom

set xmlString "<Names>
    <abc>john</abc>
    <abc>Ram</abc>
</Names>"

puts "Old:"
puts "$xmlString"

# Parse the string into an XML document
set doc [dom parse $xmlString]
set root [$doc documentElement]

foreach node [$root getElementsByTagName abc] {
    # Each node is <abc>...</abc>, the first child of this node is the
    # text node
    set nodeText [[$node firstChild] nodeValue]

    # Create a new node
    $doc createElement "name" newNode
    $newNode appendChild [$doc createTextNode $nodeText]

    # Replace the node with newNode
    $root replaceChild $newNode $node
}

# Convert back to string
set newXmlString [$doc asXML]
puts "\nNew:"
puts "$newXmlString"

Discussion

  • tdom treats each node <abc>john</abc> as two nodes: an <abc> node and a child node (whose tag name is #text). The value john is really the value of this #text node.

  • Therefore, to get the value john, I have to first get the first child of node <abc>, then get the value off that child:

    set nodeText [[$node firstChild] nodeValue]
    
  • Conversely, to create a new <name>john</name> node, I have to create two nested nodes, as seen in the code.

Update

Dealing with sub nodes requires a different approach. Here is one approach:

  1. For each <abc> node, gets an XML presentation (the xmlSubString variable)
  2. Do a textual search and replace on this XML text to replace <abc> with <name>
  3. Delete the old <abc> node
  4. Append this new XML text to the root node. This operation might change the order of the nodes within the root <Names>

Replace the old foreach block with this one:

foreach node [$root getElementsByTagName abc] {
    # Get the XML output of this node, then do a search and replace
    set mapping {"<abc>" "<name>" "</abc>" "</name>"}
    set xmlSubString [$node asXML]                       ;# Step 1
    set xmlSubString [string map $mapping $xmlSubString] ;# Step 2

    # Replace the old node with this new XML text. This operation
    # potentially changes the order of the nodes.
    $node delete                                         ;# Step 3
    $root appendXML $xmlSubString                        ;# Step 4
}
Hai Vu
  • 37,849
  • 11
  • 66
  • 93
  • Thanks for reply... suppose tag has also its child tag say "xyz" ie. is there any way to copy all the child nodes and there attributes and crete new node with those value and then replace it. john 123 Ram want to change in the form .. john 123 Ram – user2940110 Oct 31 '13 at 10:43
  • @HaiVu I am facing NOT_FOUND_ERR in the statement "$root replaceChild $newNode $node" . Can you pls help? – Shivaraj Bhat Apr 17 '19 at 12:34
  • @ShivarajBhat, I have tried my script and did not see that problem, so I don't know what I can do to help. I am running Tcl 8.6 at this time. – Hai Vu Apr 20 '19 at 19:24