XML::XUpdate::LibXMLでxupdateを使う

xmldiff xupdate XMLDB xindice の続き。

前に検索してたときにどうもxmlupdateで検索してたっぽく、今日xupdateで検索したらふつうに XML::XUpdate::LibXML – Simple implementation of XUpdate format – search.cpan.org が出てきて脱力。

<?xml version="1.0"?>
<me
        ><name>KUMAGAI Kentaro</name>
        <mail>ku*ido.nu</mail>
        <related_url>
                <url>http://ido.nu/</url>
                <url>http://twitter.com/ku</url>
                <url>http://ku.tumblr.com/</url>
        </related_url>
</me>

っていうのと

<?xml version="1.0"?>
<me>
        <name>熊谷健太郎</name>
        <mail>ku0522a*gmail.com</mail>
        <related_url>
                <url>http://ido.nu/</url>
                <url>http://twitter.com/ku</url>
                <url>http://ku.tumblr.com/</url>
                <url>http://ku.tumblr.com/</url>
                <url>http://bluedot.us/users/ku0522</url>
        </related_url>
</me>

っていうのの差分をxmldiffで作る。

xmldiff -x profile.xml profile_altered.xml

出てくるxupdateは

<?xml version="1.0"?>
<xupdate:modifications version="1.0" xmlns:xupdate="http://www.xmldb.org/xupdate">
<xupdate:append select="/me[1]"  child="first()" >  <xupdate:element name="name">熊谷健太郎
  </xupdate:element>
</xupdate:append>
<xupdate:insert-after select="/me[1]/name[1]" >  <xupdate:element name="mail">ku0522a*gmail.com
  </xupdate:element>
</xupdate:insert-after>
<xupdate:insert-after select="/me[1]/related_url[1]/url[3]" >  <xupdate:element name="url">http://ku.tumblr.com/
  </xupdate:element>
</xupdate:insert-after>
<xupdate:insert-after select="/me[1]/related_url[1]/url[4]" >  <xupdate:element name="url">http://bluedot.us/users/ku0522
  </xupdate:element>
</xupdate:insert-after>
<xupdate:remove select="/me[1]/name[2]" />
<xupdate:remove select="/me[1]/mail[2]" />
</xupdate:modifications>

こう。これを

#!/usr/bin/perl

use XML::LibXML;
use XML::XUpdate::LibXML;

$parser = XML::LibXML->new();
$dom = $parser->parse_file(shift);
$diff = $parser->parse_file(shift);

$xupdate = XML::XUpdate::LibXML->new();
$xupdate->process($dom->getDocumentElement(), $diff);

print  $dom->toString();

こういうコードで実行するだけでいい。
でもどうもxmldiffが生成するxupdateの中の child="first()" っていうのはNGらしく、実行すると

xmlXPathCompOpEval: function first not found
Unregistered function at /usr/perl5/site_perl/5.8.4/XML/XUpdate/LibXML.pm line 164

といわれるので XML/XUpdate/LibXML.pm をみてみると

    # XUpdate WD is weird:
      # child=1 should mean make the new node 1st child
      # child=last() should mean make new node last child

なーんて書いてあるので、child="first()"部分をあらかじめ child="1" に置き換えておいてあげると解決。たしかにXPathにlast()はあるけどfirst()はない。
そうするとインデントが違うけどXML的には同じものに仕上がります。

<me>  <name>熊谷健太郎
  </name>  <mail>ku0522a*gmail.com
  </mail>

        <related_url>
                <url>http://ido.nu/</url>
                <url>http://twitter.com/ku</url>
                <url>http://ku.tumblr.com/</url>  <url>http://ku.tumblr.com/
  </url>  <url>http://bluedot.us/users/ku0522
  </url>

        </related_url>
  </me>

ブラボー!

どうもxmldiffのつくるxupdateとXML::XUpdate::LibXMLと相性が良くないようで、あらかじめxupdateのXMLから不要なホワイトスペースを取っておかないと出てくる結果がおかしかったり、インデントが変になったりします。なのでこの組み合わせで使うときはかならずとりましょう。


About this entry