I have a function that takes an XML document and parses out specific XML elements and appends those to a list for use by an Oracle executemany call. The placement of the elements matters as the executemany string uses positional binds, though I could easily use a dictionary to use named binds instead. I feel that while this works, it is pretty slow since it has to reparse the data 5x per document instead of pulling the whole thing into an array once. I know xml.etree is probably what I want here instead of minidom, but I'm not sure how to implement it.
The data I'm working with is structured like this:
<troubleshooter>
<vendorsAndVersions>
<versionInfo>
<type>TAX</type>
<vendor>My vendor</vendor>
<version>My version 23121</version>
</versionInfo>
<versionInfo>
<type>OS</type>
<vendor>Windows Server 2008 R2 amd64</vendor>
<version>6.1</version>
</versionInfo>
<versionInfo>
<type>APPSERVER</type>
<vendor>JBoss Web</vendor>
<version>3.0.0-CR1</version>
</versionInfo>
<versionInfo>
<type>DATABASE</type>
<vendor>Microsoft SQL Server</vendor>
<version>10.50.1600</version>
</versionInfo>
<versionInfo>
<type>JAVA</type>
<vendor>Sun Microsystems Inc.</vendor>
<version>1.6.0_26</version>
</versionInfo>
<versionInfo>
<type>JDBC</type>
<vendor>Microsoft SQL Server JDBC Driver 3.0</vendor>
<version>3.0.1301.101</version>
</versionInfo>
</vendorsAndVersions>
...
</troubleshooter>
And the function looks like this:
def parseDocumentData(document, cusNumber):
"Take document from calling function, validate that the structure is \
correct, then process the XML elements and return the data as a list \
of lists."
# Check filename for customer number if cusNumber isn't passed.
if cusNumber == None:
cusNumber = os.path.splitext(os.path.basename(document))[0]
cusNumber = str(cusNumber).encode('ascii')
# Verify that the destination customer record exists. Source was verified by input function.
_dbCur.execute('select count(*) from pm_customer where customer_id=' + cusNumber)
cusVerify = _dbCur.fetchone()[0]
if cusVerify == 0:
_dbCur.execute('insert into pm_customer (customer_id) values (' + cusNumber + ')')
print 'Customer number,', cusNumber, 'was created. Moving on.'
else:
print 'Customer number,', cusNumber, 'exists. Moving on.'
parseDoc = minidom.parse(document)
dataSet = parseDoc.getElementsByTagName("versionInfo")
isValid = None
global dbWriteList
prepList = []
# This for loop parses the document and matches XML objects using the typePattern list.
# As each object is found, it is appended to the prepList. The prepList is then appended
# to the dbWriteList list after the for loop has completed.
for data in dataSet:
typePattern = re.compile('(TAX|OS|APPSERVER|DATABASE|JAVA)')
# Define XML elements to extract.
try:
vendorObj = data.getElementsByTagName("vendor")[0].childNodes[0].data
except:
vendorObj = 'Vendor Unavailable'
try:
versionObj = data.getElementsByTagName("version")[0].childNodes[0].data
except:
versionObj = 'Version Unavailable'
# Convert XML elements to ascii strings. This makes it easier to write the objects to the database.
vendorObj = str((vendorObj[:30] + '...')).encode('ascii') if len(vendorObj) > 30 else str(vendorObj).encode('ascii')
versionObj = str((versionObj[:65] + '...')).encode('ascii') if len(versionObj) > 65 else str(versionObj).encode('ascii')
typeObj = data.getElementsByTagName("type")[0].childNodes[0].data
if typePattern.match(typeObj):
isValid = 1
# TAX
if typeObj == xmlTypeTuple[0]:
prepList.append(versionObj)
# OS
if typeObj == xmlTypeTuple[1]:
prepList.append(vendorObj)
prepList.append(versionObj)
# APPSERVER
if typeObj == xmlTypeTuple[2]:
prepList.append(vendorObj)
prepList.append(versionObj)
# DATABASE
if typeObj == xmlTypeTuple[3]:
prepList.append(vendorObj)
prepList.append(versionObj)
# JAVA
if typeObj == xmlTypeTuple[4]:
prepList.append(vendorObj)
prepList.append(versionObj)
prepList.append(cusNumber)
if isValid == 1:
print 'File processed successfully. Moving on.'
dbWriteList.append(prepList)
completedFiles.append(document)
else:
print 'File did not contain valid XML elements:', document
failedFiles.append(document)
return
This gives me a string that looks like:
['my version 23121', 'Windows Server 2008 R2 amd64', '6.1', 'JBoss Web', '3.0.0-CR1', 'Microsoft SQL Server', '10.50.1600', 'Sun Microsystems Inc.', '1.6.0_26', 'customer number']
which gets appended to the list that is passed to the executemany command in the next function. Using executemany has cut a lot of processing time off, but look at this code and see myself repeating myself and it makes me think that this is not the best way to approach it.