XML i am parsing:
<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>
In Output XML i want to add a comment above Next_Item:
<List>
<Item>
<Price>
<Amount>100</Amount>
<!--Not-Needed-->
<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>
I tried following:
doc = etree.parse('XML1')
for id in doc.xpath('//Item/Price/Next_Item/text()'):
id = etree.Comment('Not-Needed')
root = doc.getroot()
root.insert(1, comment)
Its adding comment at the top of file instead of above 'next_item' element.
As here
root.insert(1, comment) [1 is index]
so is there a way where instead of index number i can pass a variable so i can add comment to number of places . for example whenever it finds 'next_item' it has to add a comment
output i am getting is:
<List>
<!--Not-Needed--><!--Not-Needed--><!--Not-Needed--><!--Not-Needed--><!--Not-Needed--><!--Not-Needed--><!--Not-Needed--><!--Not-Needed-->
<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>
Grateful for your help.
CodePudding user response:
Try the below (Note that no external lib is required)
import xml.etree.ElementTree as ET
xml = '''<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>'''
root = ET.fromstring(xml)
first_price = root.find('.//Item/Price')
first_price.insert(1, ET.Comment('Not-Needed'))
ET.dump(root)
output
<List>
<Item>
<Price>
<Amount>100</Amount>
<!--Not-Needed-->
<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:
I think this is a good use case for an XSLT transformation and lxml supports XSLT 1.0:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output indent="yes"/>
<xsl:template match="@* | node()" name="identity">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Item/Price[Amount = 100]/Next_Item[1]">
<xsl:comment>Not-needed</xsl:comment>
<xsl:call-template name="identity"/>
</xsl:template>
</xsl:stylesheet>
So a small Python sample using lxml to run the above XSLT against the shown sample input and write the transformation result to the file result1.xml
is e.g.
from lxml import etree as ET
def xslt_usage_example():
xml = ET.XML("""<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>""")
transformer = ET.XSLT(ET.XML("""<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="xml" indent="yes" encoding="UTF-8"/>
<xsl:template match="@* | node()" name="identity">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Item/Price[Amount = 100]/Next_Item[1]">
<xsl:comment>Not-needed</xsl:comment>
<xsl:call-template name="identity"/>
</xsl:template>
</xsl:stylesheet>"""))
result = transformer(xml)
result.write_output("result1.xml")
if __name__ == '__main__':
xslt_usage_example()