One way of doing it is to generate a really simple XML tree for each record in your spreadsheet, write an XSLT stylesheet to translate from your simple XML to ISO 19139, then use lxml.etree.XSLT to transform.
The major advantage of this is it keeps the complex iso19139 XML out of your python code which makes it much easier to debug. Some more info/tutorials on xslt here and here.
Here's some example code:
from lxml import etree
import os.path
def list_to_xml(datalist,rootname):
'''Transform a metadata record to a flat XML string'''
root = etree.Element(rootname)
for fld in datalist:
col=fld[0]
dat=str(fld[1]) # may need to be careful of unicode encoding issues
# when reading data from Excel
child=etree.SubElement(root,col)
child.text=dat
return root
def transform(inxml,xslfile):
xslfile=os.path.abspath(xslfile).replace('\\','/') #xslt doesn't like backslashes in absolute paths...
xsl = etree.parse(xslfile)
xslt = etree.XSLT(xsl)
return xslt(inxml)
xslfile = 'test.xslt'
#Column headers from the Excel spreadsheet
header=['column1','xmin','ymin','xmax','ymax','thelastcolumn']
#Data pulled out of the spreadsheet
#You would loop over all rows and do a transform on each row
row=['A value',1.2345,5.4321,2.3456,6.5432,'last value']
inxml = list_to_xml(zip(header,row),"ExcelMetadata")
print etree.tostring(inxml)
#The above prints - <ExcelMetadata><column1>A value</column1><xmin>1.2345</xmin><ymin>5.4321</ymin><xmax>2.3456</xmax><ymax>6.5432</ymax><thelastcolumn>last value</thelastcolumn></ExcelMetadata>
iso19139 = transform(inxml,xslfile)
print str(iso19139)
The 'test.xslt' stylesheet referred to in the above code is:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:fn="http://www.w3.org/2005/xpath-functions"
xmlns:xdt="http://www.w3.org/2005/xpath-datatypes"
xmlns:gco="http://www.isotc211.org/2005/gco"
xmlns:gmd="http://www.isotc211.org/2005/gmd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:gts="http://www.isotc211.org/2005/gts"
xmlns:gsr="http://www.isotc211.org/2005/gsr"
xmlns:gss="http://www.isotc211.org/2005/gss"
xmlns:gmx="http://www.isotc211.org/2005/gmx"
xmlns:gml="http://www.opengis.net/gml"
xsi:schemaLocation="http://www.isotc211.org/2005/gmd http://www.isotc211.org/2005/gmd/gmd.xsd">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/ExcelMetadata">
<gmd:fileIdentifier>
<gco:CharacterString><xsl:value-of select="column1"/></gco:CharacterString>
</gmd:fileIdentifier>
<!-- Lots of other xml stuff -->
<xsl:if test="normalize-space(thelastcolumn)">
<gmd:someelement>
<gco:CharacterString><xsl:value-of select="thelastcolumn"/></gco:CharacterString>
</gmd:someelement>
</xsl:if>
<gmd:identificationInfo>
<gmd:MD_DataIdentification>
<gmd:extent>
<gmd:EX_Extent>
<gmd:geographicElement>
<gmd:EX_GeographicBoundingBox>
<gmd:extentTypeCode>
<gco:Boolean>1</gco:Boolean>
</gmd:extentTypeCode>
<gmd:westBoundLongitude>
<gco:Decimal><xsl:value-of select="xmin"/></gco:Decimal>
</gmd:westBoundLongitude>
<gmd:eastBoundLongitude>
<gco:Decimal><xsl:value-of select="xmax"/></gco:Decimal>
</gmd:eastBoundLongitude>
<gmd:southBoundLatitude>
<gco:Decimal><xsl:value-of select="ymin"/></gco:Decimal>
</gmd:southBoundLatitude>
<gmd:northBoundLatitude>
<gco:Decimal><xsl:value-of select="ymax"/></gco:Decimal>
</gmd:northBoundLatitude>
</gmd:EX_GeographicBoundingBox>
</gmd:geographicElement>
</gmd:EX_Extent>
</gmd:extent>
</gmd:MD_DataIdentification>
</gmd:identificationInfo>
</xsl:template>
</xsl:stylesheet>