0

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.

8
  • I don't see that XPath //Item/Price/Next_Item() working at all, what are the () at the end good for? Have you considered using XSLT to insert the comment nodes? Commented Oct 20, 2022 at 8:04
  • @MartinHonnen I have changed the path to //Item/Price/Next_Item/text() I have never worked in XSLT yet Commented Oct 20, 2022 at 8:12
  • 1
    Your input sample has four Next_Item elements and your text seems to suggest you want to add a comment before each of them, yet your output sample only shows one comment before the first Next_Item element. Please clarify. Commented Oct 20, 2022 at 8:31
  • @MartinHonnen I have edited my question with XML i am getting and XML I am looking for root.insert(1, comment) only takes index but I want to pass a variable which will search the Item/Price/Id if its 100 then it will add comment above Next_Item Commented Oct 20, 2022 at 11:11
  • 1
    @mzjn sorry for inconvinience. I have reverted the question Commented Oct 20, 2022 at 16:11

2 Answers 2

1

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>
Sign up to request clarification or add additional context in comments.

Comments

1

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.