I am finding elements from XML1 in XML2 and after that i want to insert one comment XML1 is:
<Price>
<Amount>100</Amount>
<Amount>102</Amount>
<Amount>103</Amount>
</Price>
and XML2 is:
<List>
<Item>
<Price>
<Amount>100</Amount>
<Next_Item>
<Name>Apple</Name>
</Next_Item>
<Next_Item>
<Name>Orange</Name>
</Next_Item>
</Price>
<Price>
<Amount>200</Amount>
<Next_Item>
<Name>Apple</Name>
</Next_Item>
<Next_Item>
<Name>Orange</Name>
</Next_Item>
</Price>
</Item>
</List>
Output XML i want is following where I want to insert a comment above Price if the value is matching from price/amount from XML2:
<List>
<Item>
<!--important-->
<Price>
<Amount>100</Amount>
<Next_Item>
<Name>Apple</Name>
</Next_Item>
<Next_Item>
<Name>Orange</Name>
</Next_Item>
</Price>
<Price>
<Amount>200</Amount>
<Next_Item>
<Name>Apple</Name>
</Next_Item>
<Next_Item>
<Name>Orange</Name>
</Next_Item>
</Price>
</Item>
</List>
After getting help from this forum I tried coding like this:
xml1 = etree.parse('C:/Python/XML1.xml')
xml2 = etree.parse('C:/Python/XML2.xml')
for am in xml1.xpath('//Price/Id/text()'):
x = xml2.xpath(f'//List/Item/Price[./Amount/text()="{am}"]')
if len(x)>0:
root = xml2.find('.//List/Item/Price')
root.insert(1, etree.Comment('important'))
etree.dump(root)
with open('C:/Python/output.xml','w') as f:
f.write(etree.Comment(root))
I am not able to get proper output xml I am getting error: AttributeError: 'NoneType' object has no attribute 'insert'
Grateful for any kind of help.
CodePudding user response:
Since you need to compare across documents and traverse multiple nodes with conditional logic to add comment, consider XSLT, the special-purpose language designed to transform XML files. Python's lxml
library can run XSLT 1.0 scripts. Also, XSLT maintains the document
function to parse from other documents.
Specifically, below XSLT runs the identity transform to copy document as is and then conditionally adds comment before <Price>
element. Notice XSLT references the First_Document.xml
and Python references the Second_Document.xml
. Be sure file in document()
is in same location to second document.
XSLT (save as .xsl file, a special .xml file)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" encoding="utf-8" indent="yes" />
<xsl:strip-space elements="*"/>
<!-- IDENTITY TRANSFORM -->
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<!-- UPDATE Item NODES -->
<xsl:template match="Item">
<xsl:copy>
<!-- MAP THROUGH Price NODES -->
<xsl:for-each select="Price">
<!-- CONDITIONALLY ADD COMMENT -->
<xsl:variable name="curr_amount" select="Amount"/>
<xsl:if test="document('First_Document.xml')/Price/Amount = $curr_amount">
<xsl:comment>important</xsl:comment>
</xsl:if>
<!-- ALWAYS RE-WRITE Price NODES -->
<xsl:copy>
<xsl:apply-templates select="*"/>
</xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Python
from lxml import etree
# LOAD XML AND XSLT
xml = etree.parse("Second_Document.xml")
xsl = etree.parse("My_StyleSheet.xsl")
# CONFIGURE AND RUN TRANSFORMER
transformer = etree.XSLT(xsl)
result = transformer(xml)
# SAVE TRANSFORMATION TO FILE
result.write_output("Output.xml")
Output
<List>
<Item>
<!--important-->
<Price>
<Amount>100</Amount>
<Next_Item>
<Name>Apple</Name>
</Next_Item>
<Next_Item>
<Name>Orange</Name>
</Next_Item>
</Price>
<Price>
<Amount>200</Amount>
<Next_Item>
<Name>Apple</Name>
</Next_Item>
<Next_Item>
<Name>Orange</Name>
</Next_Item>
</Price>
</Item>
</List>
CodePudding user response:
Try the below
import xml.etree.ElementTree as ET
xml1 = '''<Price>
<Amount>100</Amount>
<Amount>102</Amount>
<Amount>103</Amount>
</Price>'''
xml2 = ''' <List>
<Item>
<Price>
<Amount>100</Amount>
<Next_Item>
<Name>Apple</Name>
</Next_Item>
<Next_Item>
<Name>Orange</Name>
</Next_Item>
</Price>
<Price>
<Amount>200</Amount>
<Next_Item>
<Name>Apple</Name>
</Next_Item>
<Next_Item>
<Name>Orange</Name>
</Next_Item>
</Price>
</Item>
</List>
'''
root1 = ET.fromstring(xml1)
root2 = ET.fromstring(xml2)
amounts = set(e.text for e in root1.findall('.//Amount'))
for item in root2.findall('.//Item'):
prices = item.findall('.//Price')
for price in prices:
amount = price.find('Amount').text
if amount in amounts:
item.insert(0, ET.Comment('Important'))
ET.dump(root2)
output
<List>
<Item>
<!--Important-->
<Price>
<Amount>100</Amount>
<Next_Item>
<Name>Apple</Name>
</Next_Item>
<Next_Item>
<Name>Orange</Name>
</Next_Item>
</Price>
<Price>
<Amount>200</Amount>
<Next_Item>
<Name>Apple</Name>
</Next_Item>
<Next_Item>
<Name>Orange</Name>
</Next_Item>
</Price>
</Item>
</List>