How to add comments in XML using python

111 views Asked by At

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.

2

There are 2 answers

0
balderman On BEST ANSWER

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>
0
Martin Honnen On

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()