Home > Software engineering >  How to add comments in XML using python
How to add comments in XML using python

Time:10-21

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