#!/usr/bin/env python ### =========================================================================== ### ### Modifies or add an element in an XML file ### ### =========================================================================== ## ## Warning, to install, configure, run, use any of Olivier Marti's ## software or to read the associated documentation you'll need at least ## one (1) brain in a reasonably working order. Lack of this implement ## will void any warranties (either express or implied). ## O. Marti assumes no responsability for errors, omissions, ## data loss, or any other consequences caused directly or indirectly by ## the usage of his software by incorrectly or partially configured ## personal. ## ## SVN information __Author__ = "$Author$" __Date__ = "$Date$" __Revision__ = "$Revision$" __Id__ = "$Id$" __HeadURL = "$HeadURL$" # python update_xml.py -i ~/Unix/TOOLS/MOSAIX/iodef_atm_to_oce.xml -o essai.xml -n 'context[@id="interpol_read"]/file_definition/file[@id="file_src"]/field[@id="mask_src"]' -k name=Bidon # python update_xml.py -i ~/Unix/TOOLS/MOSAIX/iodef_atm_to_oce.xml -d -o essai.xml -n 'context[@id="interpol_run"]/file_definition/file[@id="dia"]/variable[@name="title"]' -t "SRC mask interpolated to DST" # python update_xml.py -i ~/Unix/TOOLS/MOSAIX/iodef_atm_to_oce.xml -o essai.xml -c InFile.txt # Tested with python/2.7.12 and python/3.6.4 # import xml.etree.ElementTree import argparse, sys, textwrap, shlex # Check version of Python Version = sys.version_info if Version < (2,7,0) : sys.stderr.write ( "You need python 2.7 or later to run this script\n" ) sys.stderr.write ( "Present version is: " + str(Version[0]) + "." + str(Version[1]) + "." + str(Version[2]) + "\n" ) sys.exit (1) ## ============================================================================ ## Needed functions def simplify_string_list (list_str) : '''Concatenate some elements of the list of strings when needed''' zlist = list_str.copy () ; list_new = [] while ( len (zlist) > 0 ) : arg = zlist.pop (0) if arg[0] == '"' : for arg2 in zlist.copy () : arg = arg + " " + arg2 zlist.pop (0) if arg2[-1] == '"' : break arg = arg.strip('"').strip("'") list_new.append (arg) return list_new def UpdateNode (iodef, Node, Text=None, Key=None) : '''Update an xml node''' # Remove whitespaces at both ends Node = Node.rstrip().lstrip() ## Find node nodeList = iodef.findall (Node) ## Check that one and only one node is found if len (nodeList) == 0 : print ( "Error : node not found" ) print ( "Node : ", Node ) sys.exit (1) if len (nodeList) > 1 : print ( "Error : " + len (nodeList)+" occurences of node found in file" ) print ( "Node : ", Node ) sys.exit (2) ## Update element elem = nodeList[0] if Text != None : if Verbose : print ( 'Node:', Node, ' -- Text:', Text ) if Debug : print ( 'Attributes of node: ' + str (elem.attrib) ) print ( 'Text : ' + str (elem.text) ) elem.text = Text if Key != None : # Check the syntax if not '=' in Key : print ( 'Key syntax error. Correct syntax is -k Key=Value' ) sys.exit (-1) else : KeyName, Value = Key.split ('=') if Verbose : print ( 'Node:', Node, ' -- Key:', Key ) # To do : check that KeyName exist (it is added if not : do we want that ?) if Debug : print ( 'Attributes of node: ' + str (elem.attrib) ) elem.attrib.update ( { KeyName:Value } ) return iodef ## ============================================================================ ## Main code # Creating a parser to read the command line arguments # The first step in using the argparse is creating an ArgumentParser object: parser = argparse.ArgumentParser (description = """ Examples with the modification on the command line : python %(prog)s -i iodef.xml -n 'context[@id="interpol_run"]/file_definition/file[@id="file_src"]/field[@id="mask_source"]' -k name -v maskutil_T python %(prog)s -i iodef.xml -n 'context[@id="interpol_run"]/file_definition/file[@id="dia"]/variable[@name="dest_grid"]' -t dstDomainType Usage with a command file : python %(prog)s -i iodef.xml -c commands.txt Syntax in the command file : removes the quote around the node description : -n context[@id="interpol_run"]/file_definition/file[@id="dia"]/variable[@name="dest_grid"] -t dstDomainType """ + "SVN : " + __Revision__, formatter_class=argparse.RawDescriptionHelpFormatter, epilog='-------- This is the end of the help message --------') # Adding arguments group1 = parser.add_mutually_exclusive_group (required=False) parser.add_argument ( '-i', '--input' , help="XML input file" , default='iodef.xml', type=str, metavar='' ) parser.add_argument ( '-o', '--output' , help="XML output file" , default=None , type=str, metavar='' ) parser.add_argument ( '-n', '--node' , help="XML node in Xpath syntax", default=None, type=str, metavar='') group1.add_argument ( '-k', '--key' , help="XML key to update and new value (-k =)", default=None, type=str, metavar='' ) group1.add_argument ( '-t', '--text' , help="Will replace the 'text' part of the Xpath by ", default=None, type=str, metavar='' ) parser.add_argument ( '-d', '--debug' , help="Extensive debug prints", action="store_true", default=False ) parser.add_argument ( '-v', '--verbose', help="Some verbosity" , action="store_true", default=False ) parser.add_argument ( '-c', '--commandfile', help="file with list of command", default=None, type=str ) # Parse command line myargs = parser.parse_args () Verbose = myargs.verbose Debug = myargs.debug if Debug : print ( "Command line arguments : ", myargs ) FileCommand = myargs.commandfile FileIn = myargs.input FileOut = myargs.output Node = myargs.node Key = myargs.key Text = myargs.text if FileCommand != None : if ( Node != None or Key != None or Text != None ) : print ('Error : when a command file is specified, options -k|--key, -n|--node are unused' ) exit (-2) if FileOut == None : FileOut = FileIn ## Get XML tree from input file iodef = xml.etree.ElementTree.parse ( FileIn ) if FileCommand == None : ## Only one node to modify iodef = UpdateNode (iodef, Node, Key, Text) else : ## Read a list of modification commands in a command file fic = open (FileCommand, 'r') lignes = fic.readlines() for nn, ligne in enumerate (lignes) : ligne = ligne.strip ().split ('#')[0] # Remove leading and trailing blanks, and trailing comments if Debug : print (nn+1, ':', type (ligne) , ':', len (ligne) , ':', ligne, ':') if ligne == '' or ligne == None or len(ligne) == 0 or ligne[0] == '#' : if Debug : print ('Skips blank or comment line') else : list_args = shlex.split (ligne) if Debug : print ( '{:3d} : '.format(nn+1), end='') print ( list_args ) # Parse args line myargs_ligne = parser.parse_args (list_args) Node = myargs_ligne.node Key = myargs_ligne.key Text = myargs_ligne.text UpdateNode (iodef, Node, Text, Key) ## Writes XML tree to file iodef.write ( FileOut ) ## This is the end if Debug : print ('This is the end') #sys.exit (0) ### =========================================================================== ### ### That's all folk's !!! ### ### ===========================================================================