Date: 2010-10-17
Tags: python

別解: lxmlを使ってXMLの要素をソートして返す

はじめに

こんにちは、NERDです。先日、 lxmlを使ってXMLの要素をソートして返す - YAMAGUCHI::weblog というエントリが上がっていたので、XMLの変換と言えばxsltでしょう!とか思ったので実際にやってみました。xsltの適用は元記事の方でも使っているlxmlで簡単にできるのが良いですね。

サンプルコード

# -*- coding: utf-8 -*-
from lxml import etree

xml = """
<statuses>
  <status>
    <id>5</id>
    <text>spam</text>
  </status>
  <status>
    <id>1</id>
    <text>egg</text>
  </status>
  <status>
    <id>100</id>
    <text>ham</text>
  </status>
  <status>
    <id>2</id>
    <text>bacon</text>
  </status>
</statuses>
"""

xslt = """
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/statuses">
    <xsl:copy>
      <xsl:for-each select="status">
        <xsl:sort select="id" data-type="number" />
        <xsl:copy-of select="." />
      </xsl:for-each>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>
"""

def sort_by_id(xml):
    try:
        tree = etree.fromstring(xml)
        transform = etree.XSLT(etree.fromstring(xslt))
        transformed = transform(tree)
        return etree.tostring(transformed, pretty_print=True)

    except Exception, e:
        """
        rescure here.
        """
        raise e


if __name__ == '__main__':
    print xml
    print '--------------------'
    print sort_by_id(xml)

xsltの記述、けっこう長くなってしまってますね。もうちょっと短く書ける気がするんですが…。あと出来るだけタグ名に依存しないように書ければいいなあ。

実行結果

<statuses>
  <status>
    <id>5</id>
    <text>spam</text>
  </status>
  <status>
    <id>1</id>
    <text>egg</text>
  </status>
  <status>
    <id>100</id>
    <text>ham</text>
  </status>
  <status>
    <id>2</id>
    <text>bacon</text>
  </status>
</statuses>

--------------------
<statuses>
  <status>
    <id>1</id>
    <text>egg</text>
  </status>
  <status>
    <id>2</id>
    <text>bacon</text>
  </status>
  <status>
    <id>5</id>
    <text>spam</text>
  </status>
  <status>
    <id>100</id>
    <text>ham</text>
  </status>
</statuses>