I'm trying to read in the data structures from an XML file using Python by cloning various nodes (and groups of nodes), then changing some of the data within the nodes and pasting it out to a new .xml file.
I currently have this code - it is enclosed in a for loop as I am trying to create multiple nodes from one cloned node. Each new node has slightly different data from the clone:
i = 1
for alias in newchannels[:]:
#split'alias' at commas and assign to content - this bit is just to retrieve the correct data to be pasted into the nodes later
content = alias.split(',')
#extract data from array
channelname = content[0]
#ditto
ip = content[2]
port = content[3]
#get channel info
root_sChannel = root_sChannelList.getElementsByTagName("servermain:Channel")[0]
#blank channel node
root_sChannelClone = root_sChannel.cloneNode(False)
#root_sChannelClone.firstChild.data = "SimulationChannel " + str(i)
#clone servermain:Name attribute from previous xml file
servermainName = root_sChannel.getElementsByTagName("servermain:Name")[0]
#clone it
servermainNameClone = servermainName.cloneNode(True)
servermainNameClone.data = "SimulationChannel" + str(i)
#this bit prints the data fine
print servermainNameClone.data
#servermainNameClone.setAttributeNode("Simulation Channel" + str(i))
#append to channel clone
root_sChannelClone.appendChild(servermainNameClone)
#this bit still prints the data fine
print servermainNameClone.data
#append other data here
root_sChannelListClone.appendChild(root_sChannelClone)
i+=1
The problem that I am having is that although the data seems to be setting fine before appending the nodes at the end (as checked by the print statements), when I actually check the outputted file, it has actually just taken the 'deeper' data of the '.cloneNode(True)' node and copied it straight over with no change, which makes absolutely no sense to me. Am I using the wrong data changing function or something?
Edit: Please find attached the full code:
#include necessary modules
import xml.dom.minidom
import csv
import os
import argparse
import sys
#set defaults
WELLS_FILE = "wells.csv"
CONFIG_FILE = "OPCServerConfig.xml"
UPDATE_FILE = "UpdatedConfig.xml"
#set constants for the column locations in CSV file
_Tenement=0
_Well=1
_Type=2
_Descr=3
_RealIP=4
_TestIP=5
_TestPort=6
#set port
_PORT="5020"
global FILES
def parseInput():
#set up argument parser
parser = argparse.ArgumentParser(description='Update Kepware Server Configuration with wells information.')
#add arguments (parameters) to the parser - the info needed when running the program
parser.add_argument('-wf','--wells', default=WELLS_FILE, help='wells file' )
parser.add_argument('-cf','--config', default=CONFIG_FILE, help='current configuration file')
parser.add_argument('-of','--output', default=UPDATE_FILE, help='updated configuration file')
global FILES
#above info stored until this line when they are actually used - then creates the necessary objects. sys.arg[1] reads argument 2 onwards from command line
FILES = parser.parse_args(sys.argv[1:])
#print out files names
print FILES.wells
print FILES.config
print FILES.output
#reads in well info
def getWellsInfo():
#set up empty array
lines = []
#set header to true by default (i.e. it hasn't found the header yet)
header = True
#open the wells.csv file, to be read in binary mode ('rb')
with open( FILES.wells, 'rb') as wellfile:
#assign tothe variable reader the wellfile
reader = csv.reader(wellfile)
#for each row in the csv file
for row in reader:
#if row is empty, or starts with '//' then go to next loop of for statement
if len(row)==0 or row[0].startswith("//") or not row[0]:
continue
#if header found, set value to false, then go to next loop
if header:
header = False
continue
#assign info from csv file to applicable arrays
#t_nm = row[ _Tenement ]
#w_nm = row[ _Well ]
#real_ip_nm = row[ _RealIP ]
test_ip_nm = row[ _TestIP ]
#set up construct for channel name
#channel = "B%(tnm)03d-w%(wnm)03d" % {'tnm':int(t_nm), 'wnm':int(w_nm) }
channel = row[ _Descr ]
# alias = "ARG_WH%(wnm)02d" % {'wnm':int(w_nm) }
#alias = "B%(tnm)03d-w%(wnm)03d" % {'tnm':int(t_nm), 'wnm':int(w_nm) }
alias = row[ _Descr ]
port = row[ _TestPort ]
#print out info to command prompt
print channel + ',' + alias + ',' + test_ip_nm.split('/')[0] + ',' + port
#append info contructs to lines array
lines.append(channel + ',' + alias + ',' + test_ip_nm.split('/')[0] + ',' + port)
wellfile.close()
return lines
def updateCfgFile(newchannels):
from xml.dom.minidom import parse
#parse the xml file and assign it to 'doc'
doc = parse(FILES.config)
#get the first 'Matrikon.OPC.ScadaModbus' element by tag name and assign it to root
root = doc.getElementsByTagName("servermain:Project")[0]
#clone (copy) the node, but not a 'deep' (children) copy, as specified by the 'false' parameter
rootClone = root.cloneNode(False)
#get the title element and assign it to the variable root_sTitleClone
root_sTitle = root.getElementsByTagName("servermain:Title")[0]
#clone the node but not a deep clone
root_sTitleClone = root_sTitle.cloneNode(False)
#as above for comments
root_sComments = root.getElementsByTagName("servermain:Comments")[0]
root_sCommentsClone = root_sComments.cloneNode(False)
#as above for AliasList
root_sAliasList = root.getElementsByTagName("servermain:AliasList")[0]
root_sAliasListClone = root_sAliasList.cloneNode(False)
#as above for GlobalDriverSettingsList, copy all child stuff too (cloneNode:True)
root_sGDSL = root.getElementsByTagName("servermain:GlobalDriverSettingsList")[0]
root_sGDSLClone = root_sGDSL.cloneNode(True)
#append the children to the rootClone hierarchy
rootClone.appendChild( root_sTitleClone )
rootClone.appendChild( root_sCommentsClone )
rootClone.appendChild( root_sAliasListClone )
rootClone.appendChild( root_sGDSLClone )
#as above for ChannelList
root_sChannelList = root.getElementsByTagName("servermain:ChannelList")[0]
root_sChannelListClone = root_sChannelList.cloneNode(False)
#for loop interation
i=1
#create array 'newchannels' and cycle through it using indexes 'alias'
for alias in newchannels[:]:
#split'alias' at commas and assign to content
content = alias.split(',')
#extract data from array
channelname = content[0]
#ditto
ip = content[2]
port = content[3]
#get channel info
root_sChannel = root_sChannelList.getElementsByTagName("servermain:Channel")[0]
#blank channel node
root_sChannelClone = root_sChannel.cloneNode(False)
#root_sChannelClone.firstChild.data = "SimulationChannel " + str(i)
#clone servermain:Name attribute from previous xml file
servermainName = root_sChannel.getElementsByTagName("servermain:Name")[0]
#clone it
servermainNameClone = servermainName.cloneNode(True)
servermainNameClone.data = "SimulationChannel" + str(i)
print servermainNameClone.data
#servermainNameClone.setAttributeNode("Simulation Channel" + str(i))
#append to channel clone
root_sChannelClone.appendChild(servermainNameClone)
print servermainNameClone.data
#append other data here
root_sChannelListClone.appendChild(root_sChannelClone)
i+=1
#sDriver = root_sChannel.getElementsByTagName("servermain:Driver")
#root_sChannelClone.appendChild(sDriver)
#set name of channel clone
#root_sChannelClone_ServerName.setAttribute("servermain:Name", "Simulation Channel " + str(i))
#root_sChannelCloneDeviceList = root_sChannel.getElementsByTagName("servermain:DeviceList")
#root_sChannelCloneDevice = root_sChannelCloneDeviceList.getElementsByTagName("servermain:Device")[0]
#root_sChannelCloneDevice.setAttribute("servermain:Name", channelname)
#root_sChannelCloneDeviceList.setAttribute("servermain:Name", "value inserted here")
#root_sChannelCloneDevice.setAttribute("servermain:ID", ip)
#root_sChannelCloneDeviceList.setAttribute("servermain:ID", "IP Inserted here")
#root_sChannelCloneDevice.setAttribute("Modbus_ethernet:Port", port)
#root_sChannelCloneDeviceList.setAttribute("Modbus_ethernet:Port", "portnumber here")
#root_sChannelListClone.appendChild ( root_sChannelClone )
rootClone.appendChild( root_sChannelListClone )
#rootClone.appendChild( root_sChannelListClone )
'''
#print out channel name, ip and port
for channel in rootDevLinkClone.getElementsByTagName("CNetworkChannelDevLink"):
name = channel.getAttribute("name")
devlink = channel.getElementsByTagName("CHostDevLink")[0]
ip = devlink.getAttribute("host")
port = devlink.getAttribute("service")
print name + ' ' + ip + ' ' + port
'''
#PSTAliasGroup = rootAlias.getElementsByTagName("PSTAliasGroup")[0]
#for alias in newchannels[:]:
# channelname = alias.split(',')[0]
# aliasname = alias.split(',')[1]
# GroupClone = PSTAliasGroup.cloneNode(True)
# GroupClone.setAttribute("name",aliasname)
# Taglist = GroupClone.getElementsByTagName("PSTAlias")
# for tag in Taglist[:]:
# pathlist = tag.getAttribute("itemPath").split('.')
# newpath = channelname
# for pathfrag in pathlist[1:]:
# newpath += '.' + pathfrag
# tag.setAttribute("itemPath", newpath)
# rootAliasClone.appendChild(GroupClone)
print "Saving..."
## This makes a second, larger copy of the XML data !!
# xmlString = rootClone.toprettyxml()
#output the file
outputfile = open(FILES.output, 'w')
#outputfile.write(xmlString)
#write xml of the root clone hierarchy
rootClone.toprettyxml(indent=' ')
rootClone.writexml(outputfile, " ", " ", "\n");
outputfile.close()
#print xmlString
#call the parse input function - sets up user argument validating etc
parseInput()
print FILES
#call the get wells info function - retrieves all necessary info from the wells.csv file
ips = getWellsInfo()
print ips
#call update cfg file
updateCfgFile(ips)