Google Protocol Buffersしてみた
これ Developer Guide | Protocol Buffers | Google Developers。
まずは、GitHub - protocolbuffers/protobuf: Protocol Buffers - Google's data interchange formatからコンパイラとソースをダウンロード。
で、protoc.exeを「protobuf-2.0.0beta/src」にコピーして 「protobuf-2.0.0beta/java/README.txt」の記述どおりにMavenでごにょごにょっとjarを作る。
次にXMLでいうDTDみたいなものを作成。こんなかんじ。とりあえず、ファイル名は「rss_test.proto」としました。
package prototest; option java_package ="koyane.prototest"; option java_outer_classname = "RssTest"; message Item { required string title = 1; required string link = 2; optional string description =3; required string creator = 4; required string date = 5; optional string subject = 6; } message RssChannel { required string title = 1; required string link = 2; optional string description =3; repeated Item items =4; }
なんとなーくjavaとかC++ぽい表記。リファレンスよまなくても何だかわかりそう。雰囲気で。
「option java_package」は生成されるJavaクラスのパッケージ。指定されてなければ「package」の記述が適用されるらしい。
「option java_outer_classname」は生成されるJavaクラスの名称。指定されてなければファイル名がクラスの名前に使われるみたい。この例でいくと、「rss_test.proto」だから、「RssTest」みたいな。
message XXX{ ... }
の部分は生成されるJavaクラスのインナークラスに該当してました。
この例ではstringしか使ってないけどint32(int)とかint64(long)とかbool(boolean)とかあります。
required は必須フィールド
optional は必須じゃないフィールド
repeated はまあListになる感じ
試してないけどmessageを宣言する順序は関係ありそう。サンプル見る限り。
で、.protoファイルを作成したら、コンパイラ protoc.exe の出番
protoc --java_out=/ rss_test.proto
でとりあえず、ファイルシステムのルートにJavaソースコードが生成されます。この例だと、koyane.prototest.RssTest.java。
ふー。やっと準備完了。ここからが読み書きです。
まずはインスタンスを生成してファイルに保存
package koyane.prototest; import java.io.FileOutputStream; import java.util.Iterator; import javax.xml.XMLConstants; import javax.xml.namespace.NamespaceContext; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathFactory; import koyane.prototest.RssTest.Item; import koyane.prototest.RssTest.RssChannel; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.methods.GetMethod; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; public class TestWrite { public static void main(String[] args) throws Exception{ Document doc = read("http://d.hatena.ne.jp/koyane/rss"); Element root = doc.getDocumentElement(); XPathFactory factory = XPathFactory.newInstance(); XPath xpath = factory.newXPath(); xpath.setNamespaceContext(new NamespaceContext(){ @Override public String getNamespaceURI(String prefix) { if(prefix.equals("dc")){ return "http://purl.org/dc/elements/1.1/"; }else{ return XMLConstants.NULL_NS_URI; } } @Override public String getPrefix(String namespaceURI) { throw new UnsupportedOperationException(); } @Override public Iterator<?> getPrefixes(String namespaceURI) { throw new UnsupportedOperationException(); } }); Node cNode = (Node)xpath.evaluate("channel", root, XPathConstants.NODE); //ここ。ここがProtocol Buffers。 RssChannel.Builder channel = RssChannel.newBuilder(); channel.setTitle(xpath.evaluate("title/text()", cNode)); channel.setLink(xpath.evaluate("link/text()", cNode)); channel.setDescription(xpath.evaluate("description/text()", cNode)); NodeList iList = (NodeList)xpath.evaluate("item", root, XPathConstants.NODESET); for (int i=0;i<iList.getLength();i++) { Node iNode = iList.item(i); //あとここ。ここがProtocol Buffers。 Item.Builder item = Item.newBuilder(); item.setTitle(xpath.evaluate("title/text()", iNode)); item.setLink(xpath.evaluate("link/text()", iNode)); item.setDescription(xpath.evaluate("description/text()", iNode)); item.setCreator(xpath.evaluate("creator/text()", iNode)); item.setDate(xpath.evaluate("date/text()", iNode)); item.setSubject(xpath.evaluate("subject/text()", iNode)); channel.addItems(item.build()); } //で、ファイルに書き出し。 FileOutputStream output = new FileOutputStream("c:/rss_test_result"); channel.build().writeTo(output); output.close(); } private static Document read(String url) throws Exception{ HttpClient httpclient = new HttpClient(); GetMethod method = new GetMethod(url); httpclient.executeMethod(method); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document doc = builder.parse(method.getResponseBodyAsStream()); method.releaseConnection(); return doc; } }
うりゃーーーっと、はてなのRSSを読み込んで保存。ひでーこーどだ。Protocol Buffersのところは超簡単。
次にこのファイルを復元。
package koyane.prototest; import java.io.FileInputStream; import koyane.prototest.RssTest.Item; import koyane.prototest.RssTest.RssChannel; public class TestRead { public static void main(String[] args) throws Exception{ RssChannel channel = RssChannel.parseFrom(new FileInputStream("c:/rss_test_result")); System.out.println("title : " + channel.getTitle()); System.out.println("link : " + channel.getLink()); System.out.println("description : " + channel.getDescription()); for (Item item : channel.getItemsList()) { System.out.println(); System.out.println("title : " + item.getTitle()); System.out.println("link : " + item.getLink()); System.out.println("description : " + item.getDescription()); System.out.println("creator : " + item.getCreator()); System.out.println("date : " + item.getDate()); System.out.println("subject : " + item.getSubject()); } } }
おお、できた。
今のところ私に使い道はないけど、面白いなあ。