JavaでXMLを簡単に解析する方法
JAXB(Java Architecture for XML Binding)を使う方法を,Yahoo校正支援APIを題材に解説します.
どこら辺が「簡単」なのか?
各種API系のサービスで配布されている,XMLスキーマファイル(xsd)から,Javaクラスファイル群を自動生成することで,
自前のコードはほんの一握り(テンプレートにできそうなぐらい,一部のみです)になり,かつ自前でParseすることを考えたら,手間も品質も雲泥の差です.
正規表現で無理やり,とか怪しすぎるし,SAXやDOMを使えば意のままにコーディングできるけど拡張大変だし.
というわけで,JAXBに丸投げします.
前提環境
- JDK6
- Ubuntu Desktop 10.04(Windowsでもできるとは思いますが)
- Yahoo!デベロッパーネットワークのアプリケーションIDは取得済み
0. JAXB用のクラスファイルの自動生成
まず,http://jlp.yahooapis.jp/KouseiService/V1/kousei.xsd をダウンロードします.次に,Terminalで,以下のコマンドを入力
$ xjc kousei.xsd
すると,以下のディレクトリ,ファイルが生成されます.
./yahoo `-- jp `-- jlp `-- kouseiservice |-- ObjectFactory.java |-- ResultSet.java |-- WordType.java `-- package-info.java
私は,この後に,kouseiserviceと同じところにkousei.xsdを移動しました.
1. サクッとテストプログラム作成
Kousei.javaを作成
import java.net.URL; import java.net.URLEncoder; import javax.xml.XMLConstants; import javax.xml.bind.*; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import org.xml.sax.SAXException; import yahoo.jp.jlp.kouseiservice.*; /** * * @author Syo Takasaki */ public class Kousei { private static String appid = "dummy"; private static String appuri = "http://jlp.yahooapis.jp/KouseiService/V1/kousei"; private static String path_xsd = "yahoo/jp/jlp/kouseiservice/kousei.xsd"; private static URL schemaURL; private static Schema schema; private static String path_context = "yahoo.jp.jlp.kouseiservice"; private static JAXBContext context; private static Unmarshaller unmarshaller; public static void main(String[] args) throws Exception{ Kousei exe = new Kousei(); ResultSet results = exe.getResults("遙か彼方に小形飛行機が見える."); System.out.println("SENTENCE:遙か彼方に小形飛行機が見える."); for(WordType data : results.getResult()){ System.out.print(data.getStartPos() + "\t"); System.out.print(data.getLength() + "\t"); System.out.print(data.getSurface() + "\t"); System.out.print(data.getShitekiInfo() + "\t"); System.out.println(data.getShitekiWord()); } results = exe.getResults("モルジブ大学行く.大学行く."); System.out.println("SENTENCE:モルジブ大学行く.大学行く."); for(WordType data : results.getResult()){ System.out.print(data.getStartPos() + "\t"); System.out.print(data.getLength() + "\t"); System.out.print(data.getSurface() + "\t"); System.out.print(data.getShitekiInfo() + "\t"); System.out.println(data.getShitekiWord()); } } public Kousei() throws SAXException, JAXBException{ SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); schemaURL = this.getClass().getClassLoader().getResource(path_xsd); schema = factory.newSchema(schemaURL); context = JAXBContext.newInstance(path_context); unmarshaller = context.createUnmarshaller(); unmarshaller.setSchema(schema); } public ResultSet getResults(String sentence){ ResultSet results = null; try{ URL _url = new URL(appuri + "?appid=" + appid + "&sentence=" + URLEncoder.encode(sentence, "UTF-8")); results = (ResultSet)unmarshaller.unmarshal(_url); }catch (Exception e){ System.err.println("ERROR:" + e); } return results; } }
2. 実行
$ javac Kousei.java $ java Kousei SENTENCE:遙か彼方に小形飛行機が見える. 0 2 遙か 表外漢字あり ●か 2 2 彼方 用字 彼方(かなた)、かなた 5 5 小形飛行機 誤変換 小型飛行機 SENTENCE:モルジブ大学行く.大学行く. 0 4 モルジブ 外国地名 モルディブ 9 4 大学行く 助詞不足の可能性あり
後書きと懺悔
きっかけは,Play framework上でGroovyがうまく使えなかったから(テンプレートエンジンはGroovyだけど,それ以外のところで使いたかった).
「GroovyならサクッとXMLパースもできるんだけど……Pure Javaか」……と意気消沈したところで,調べてみたわけ.
で,エイヤと作ったはいいものの,エラーハンドリングは適当過ぎるし,いくつかのサイトを参考にしたからコーディングスタイルもごちゃごちゃだし.
そもそも引数でテスト文字列与えられないってどういうことよ,自分orz
でも動いたから満足してしまった.path_xsdとpath_contextが良く分からず,いろんな指定の組み合わせをがんばって,一番良さげな書き方に落ち着いたところが一番満足.
くれぐれも「このまま」は使わないように.
なお,生成は以下のURLの1番目を参考にしてください.
marshallerを追加するだけでいけると思います.