How to merge XML string with XML created by objectify?
Question: I am using python 2.7

I currently have a procedure in place that generates orders in XML from csv
data that works. However everything is hardcoded, and I want to make it a bit
more dynamic as I expand the code to fit more clients.

As it stands, I have a section that adds basic info such as ship to and bill
to address to the order

    
    
    from lxml import objectify
    E = objectify.E
    fileElem = E.request(
        E.customerID("###"),
        E.userID("####"),
        E.btNameCompany("BillToCompany"),
        E.btAttention("John Snow"),
        E.btStreet("123 Any Street"),
        E.btAddress2(),
        E.btAddress3(),
        E.btCity("City"),
        E.btState("State"),
        E.btZip("12345"),
        E.btCountry("USA"),
        E.btTelephone(),
        E.btEmail(),
        E.customerPO(customerPO),
        E.stNameCompany(shipToCompany),
        E.stAttention(shipToAttn),
        E.stStreet(shipToAddr),
        E.stAddress2(shipToAddr2),
        E.stCity(shipToCity),
        E.stState(shipToState),
        E.stZip(shipToZip),
        E.stCountry(shipToCountry),
        E.shipMethod("FedEx Ground"),
        E.stTelephone(shipToPhone),
        E.shipNotificationEmail(shipToEmail),
        E.shipperID("ShipperCompanyID"),
        E.messages(),
        )
    

From there I've been appending product info by calling a function that returns
a product built the same way, with hardcoded tags.

    
    
    def getProductXML(sku, qty):
        productList = {"Company Brochure":
           E.item(
               E.quantity(qty),
               E.productID("ID #"),
               E.productDesc("Description")
                  ),
          "Company Product":
           E.item(
               E.quantity(qty),
               E.productID("ID #"),
               E.productDesc("Description")
                  ),}
         return productList[sku]
    
    
    fileElem.append(getProductXML(sku, qty))
    

I've made some adjustments that allows me to add whatever tags a particular
item needs by setting up an excel file. Each item occupies two rows. One row
is tags, the other row is content, with a sku in the first column as an
identifier.

    
    
    sku    |    qty    |    product ID    |    random attribu    |    random attribu2
    sku    |    1      |     thing1       |    English           |     z fold
    

So I have added a bit of code that runs through the excel document and
generates XML as string. I chose to create a string because running a loop
within objectify caused errors. I was also not able to use variables as the
tags, so if variable = "quantity", `E.variable(qty)` becomes
`<variable>qty</variable>` rather than `<quantity>qty</quantity>`.

    
    
    from lxml import etree as ET
    def GetItemXML(sku,qty)
    
        def HeaderRows(r_sheet, rows, cols ):
            headerRows = {}
            for row in range(0,rows):
    
                if row%2 == 0:
                    headerRow = []
                    for col in range(0,cols):
                        if col == 0:
                            thecell=r_sheet.cell(row-1,col)
                            headerSKU = str(thecell.value)
    
    
                        else:
                            thecell=r_sheet.cell(row,col)
                            header = str(thecell.value)
                            headerRow.append(header)
    
                    headerRows[headerSKU] = headerRow
    
                else: continue
    
            return headerRows
    
    
    
        def ContentRows(r_sheet, rows, cols ):
            contentRows = {}
            for row in range(0,rows):
                if row%2 == 1:
                    contentRow = []
                    for col in range(0,cols):
                        if col == 0:
                            thecell=r_sheet.cell(row,col)
                            contentSKU = str(thecell.value)
    
    
                        else:
                            thecell=r_sheet.cell(row,col)
                            content = str(thecell.value)
                            contentRow.append(content)
    
                    contentRows[contentSKU] = contentRow
        ##            print contentSKU
    
    
                else: continue
            return contentRows
    
        def getItemXML(sku, qty, contentRows, headerRows):
            skuExists = True
    
            string = ""
            ##################################### INSERT LOGIC TO ADD PROPER PRICING, QTY, AND SHIPPING WEIGHT BASED ON QTY ENTERED  ####################################
            try:
                columnIndex = 0
                for column in headerRows[sku]:
                    string = string + ET.tostring(E(column, contentRows[sku][columnIndex]))
                    columnIndex +=1
            except: skuExists = False
    
            return string, skuExists
    

This outputs the following string

    
    
    '<quantity>1</quantity><numPages>1</numPages><padding>0</padding><versions>0</versions><price>$$.$$</price><productID>ProdID#</productID><productDesc>Product Description</productDesc><device>0</device><inksF>4</inksF><inksB>0</inksB><runW>##.00000</runW><runH>##.00000</runH><cutW>##.00000</cutW><cutH>##.00000</cutH><productType>Inventory</productType><coatingID>0</coatingID><foldingID></foldingID><scoreID>0</scoreID><bindID>0</bindID><proofID>0</proofID><mailID>0</mailID><listID>0</listID><customerDataListID>0</customerDataListID><note></note><memo></memo><weight>16.0</weight><isCanvas></isCanvas><artID>28933</artID><mapingID>0</mapingID><customerApproval>accepted</customerApproval>'
    

However I cannot turn this string back into XML

    
    
    >>> root = objectify.fromstring(string)
    
    Traceback (most recent call last):
      File "<pyshell#15>", line 1, in <module>
        root = objectify.fromstring(string)
      File "lxml.objectify.pyx", line 1802, in lxml.objectify.fromstring (src/lxml/lxml.objectify.c:22312)
      File "lxml.etree.pyx", line 3032, in lxml.etree.fromstring (src/lxml/lxml.etree.c:68292)
      File "parser.pxi", line 1786, in lxml.etree._parseMemoryDocument (src/lxml/lxml.etree.c:102641)
      File "parser.pxi", line 1674, in lxml.etree._parseDoc (src/lxml/lxml.etree.c:101470)
      File "parser.pxi", line 1074, in lxml.etree._BaseParser._parseDoc (src/lxml/lxml.etree.c:96652)
      File "parser.pxi", line 582, in lxml.etree._ParserContext._handleParseResultDoc (src/lxml/lxml.etree.c:91461)
      File "parser.pxi", line 683, in lxml.etree._handleParseResult (src/lxml/lxml.etree.c:92647)
      File "parser.pxi", line 622, in lxml.etree._raiseParseError (src/lxml/lxml.etree.c:91943)
    XMLSyntaxError: Extra content at the end of the document, line 1, column 14
    >>> 
    

OR

    
    
    >>> ET.XML(string)
    
    Traceback (most recent call last):
      File "<pyshell#12>", line 1, in <module>
        ET.XML(string)
      File "lxml.etree.pyx", line 3012, in lxml.etree.XML (src/lxml/lxml.etree.c:68047)
      File "parser.pxi", line 1786, in lxml.etree._parseMemoryDocument (src/lxml/lxml.etree.c:102641)
      File "parser.pxi", line 1674, in lxml.etree._parseDoc (src/lxml/lxml.etree.c:101470)
      File "parser.pxi", line 1074, in lxml.etree._BaseParser._parseDoc (src/lxml/lxml.etree.c:96652)
      File "parser.pxi", line 582, in lxml.etree._ParserContext._handleParseResultDoc (src/lxml/lxml.etree.c:91461)
      File "parser.pxi", line 683, in lxml.etree._handleParseResult (src/lxml/lxml.etree.c:92647)
      File "parser.pxi", line 622, in lxml.etree._raiseParseError (src/lxml/lxml.etree.c:91943)
    XMLSyntaxError: Extra content at the end of the document, line 1, column 14
    

Can someone please recommend a way to combine the xml in string form with the
xml I am generating through objectify?

Thank you.


Answer: The output string as you posted above is not a valid XML. XML only allowed to
have one root element, but your string has multiple root elements, something
like :

    
    
    <quantity>1</quantity>
    <numPages>1</numPages>
    <padding>0</padding>
    .....
    .....
    

One possible way around this is by wrapping the string within single root
element, for example :

    
    
    valid_xml_string = '<root>' + string + '</root>'
    root = objectify.fromstring(valid_xml_string)
    

