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.

Anonymous
  • 59
  • 6
  • 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? – Martin Honnen Oct 20 '22 at 08:04
  • @MartinHonnen I have changed the path to //Item/Price/Next_Item/text() I have never worked in XSLT yet – Anonymous Oct 20 '22 at 08: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. – Martin Honnen Oct 20 '22 at 08: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 – Anonymous Oct 20 '22 at 11:11
  • @MartinHonnen As I have never used XSLT yet. so I wanted to know whether I have to import anything before using it as while using `` I am getting Unresolved reference 'stylesheet' – Anonymous Oct 20 '22 at 12:24
  • I have edited my answer with a Python sample showing how to use lxml to run the XSLT sample in the answer against the XML sample in your question. – Martin Honnen Oct 20 '22 at 12:51
  • @Anonymous: The latest edit (rev 7) changed the question substantially. Don't move the goalposts. You have already received two answers. Please ask a new question. – mzjn Oct 20 '22 at 15:57
  • 1
    @mzjn sorry for inconvinience. I have reverted the question – Anonymous Oct 20 '22 at 16:11

2 Answers2

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()
Martin Honnen
  • 160,499
  • 6
  • 90
  • 110
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>
balderman
  • 22,927
  • 7
  • 34
  • 52