Sorting child elements with lxml based on attribute value

Viewed 4949

I'm trying to sort some child elements in a document based on an attribute value, and while the actual sorted function seems to be working, the splicing of the newly sorted elements doesn't seem to be.

from lxml import etree

def getkey(elem):
    # Used for sorting elements by @LIN.
    # returns a tuple of ints from the exploded @LIN value
    # '1.0' -> (1,0)
    # '1.0.1' -> (1,0,1)
    return tuple([int(x) for x in elem.get('LIN').split('.')])

xml_str = """<Interface>
                <Header></Header>
                <PurchaseOrder>
                    <LineItems>
                        <Line LIN="2.0"></Line>
                        <Line LIN="3.0"></Line>
                        <Line LIN="1.0"></Line>
                    </LineItems>
                </PurchaseOrder>
            </Interface>"""

root = etree.fromstring(xml_str)
lines = root.findall("PurchaseOrder/LineItems/Line")
lines[:] = sorted(lines, key=getkey)
res_lines = [x.get('LIN') for x in lines]
print res_lines

print etree.tostring(root, pretty_print=True)

When I execute the above code I will see the lines list did sort correctly as it prints ['1.0', '2.0', '3.0']. However the XML tree isn't updated as tostring() prints out the below.

<Interface>
  <Header/>
  <PurchaseOrder>
    <LineItems>
      <Line LIN="2.0"/>
      <Line LIN="3.0"/>
      <Line LIN="1.0"/>
    </LineItems>
  </PurchaseOrder>
</Interface>

I got the idea for how to sort from http://effbot.org/zone/element-sort.htm, which says that the splicing should be all that I need to update the element order, but that doens't seem to be the case. I realise lxml is not 100% compatible with elementtree, so as a sanity check I replaced the lxml import with elementtree and got the exact same results.

1 Answers
Related