公式:google-http-java-client Google Http Java ClientはGoogleが提供する各種 API用のライブラリー (Google APIs Client Library for Java) のベースとなるHTTP接続部分や、XML・JSONの解析などをライブラリー化したものです。 Googleのサービスは公式のGoogle APIs Client Library使えばいいので、必要ありませんが Google以外のXML・JSONサービスと連携する時に使うと便利でしょう。 Apache2.0のオープンソースで公開されています。 XMLを解析するGoogle Http Java ClientでのXML変換は、アノテーションを使って簡単なクラスPOJOを作るだけです。他のObject/XML マッピングは詳しくないので、比較できませんが、記述量が少なくて好感持てます。 例えば、こういうXMLをオブジェクトに変換するために記述が必要なのは <test> <item key="k1">hello</item> <item>value2</item> </test> たったの、これだけです。 public static class Root { @Key List<Item> item; } public static class Item{ @Key("text()") public String text; @Key("@key") String key=""; } XMLをObjectに変換するマッピングするための、コードは少ないですが、実際の変換までは多少手続きがいります。import java.io.IOException; import java.util.List; import com.google.api.client.http.GenericUrl; import com.google.api.client.http.HttpRequest; import com.google.api.client.http.HttpRequestFactory; import com.google.api.client.http.HttpRequestInitializer; import com.google.api.client.http.HttpResponse; import com.google.api.client.http.HttpTransport; import com.google.api.client.http.apache.ApacheHttpTransport; import com.google.api.client.http.xml.XmlHttpParser; import com.google.api.client.util.Key; import com.google.api.client.xml.XmlNamespaceDictionary; public class XmlToObject { public static XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); public static void main(String[] args) { namespaceDictionary.set("", ""); ApacheHttpTransport transport=new ApacheHttpTransport(); HttpRequestFactory factory=createRequestFactory(transport); try { HttpRequest request=factory.buildGetRequest(new GenericUrl("http://YOUR_HOST/test.xml")); HttpResponse response=request.execute(); System.out.println(response.parseAs(Root.class)); } catch (IOException e) { e.printStackTrace(); } } public static class Root { @Key String name; } public static class Item{ @Key("text()") public String text; @Key("@key") String key=""; public String toString(){ return "key="+key+","+text; } } public static HttpRequestFactory createRequestFactory(HttpTransport transport) { return transport.createRequestFactory(new HttpRequestInitializer() { @Override public void initialize(HttpRequest request) throws IOException { XmlHttpParser parser=XmlHttpParser.builder(namespaceDictionary).setContentType("text/xml").build(); request.addParser(parser); } }); } } テスト元のXML <test> <item key="k1"></item> <item>value2</item> <item/> <item key="k3"> </item> <item>hello<dummy/>world</item> </test> 実行の流れRequestを処理するFactoryを作る。 XMLHttpParserを作成する Requestを処理してResponseを返す 中で、Parseしてもらう。 まずは、ApacheHttpを使ったHttpRequestFactoryを作ります。 ApacheHttpTransport transport=new ApacheHttpTransport(); HttpRequestFactory factory=createRequestFactory(transport); このFactoryクラスは、HttpRequestInitializerを実装したのを引数で渡して作ります。 少し、ややこしくなるます。おそらくスレッドセーフのために、こういう仕様になっているのだと思います。 さらに、またややこしいのですが、XmlHttpParser はこういうBuilder形式で作ります。 引数のnamespaceDictionaryですが、別の所で初期化してあります。これは別の所で説明します。 あと、SetでなくAddなのは、text/xmlの処理用のパーサーだからです。 public static HttpRequestFactory createRequestFactory(HttpTransport transport) { return transport.createRequestFactory(new HttpRequestInitializer() { @Override public void initialize(HttpRequest request) throws IOException { XmlHttpParser parser=XmlHttpParser.builder(namespaceDictionary).setContentType("text/xml").build(); request.addParser(parser); } }); } あとは、リクエストしてマップさせたいクラスを渡して解析するだけです。普通xmlを呼び出すと、 コンテントタイプは、text/xmlで返すので、先程登録したパーサーが処理します。ちなみに、text/plainで返ってくると処理できません。 HttpRequest request=factory.buildGetRequest(new GenericUrl("http://YOUR_HOST/test.xml")); HttpResponse response=request.execute(); System.out.println(response.parseAs(Root.class)); 続けて、マッピングの決まりごとを説明します。 XML/Object マッピングの決まりごと実行結果 MyTest key=k1,null
key=,value2
key=,null
key=k3,
key=,world まずParserに渡すクラスですが、クラス名は何でもいいです。 そして、最初の@Keyというのが マッピングするというアノテーションです。 通常、キーにはマップ先を指定しますが、変数名と同じなら付けなくてもいいです。 @Key String name;と@Key("name") String name; は同じになります。 こう指定すると、ルートのXML中の<name>MyTest</name>を取り出せます。 次の複数の場合は、リストを使います。そしてマップ先のクラスをしていします。 ここだと、itemをマップするので名前で指定しています。 @Key("item") List<Item> items; そして、属性の値を取る場合は、アノテーションで@Key("@key")という風に@属性名になります。 そして、自分の中のテキストを取り出す場合は、@Key("text()")と、text()というメソッドを使います。 出力でnullになっているのは、値がなくて、セットされてないからです。 String key=""; という風に初期値を入れるのがいいでしょう。 あとテキスト取り出して改行が入っているはそういう仕様です。 さらに、複数のテキストの場合は、text()だと最後のテキストしか取れません。 あと、@Valueと@Nullというアノテーションがあります。 これは、リストのようなEnumの値を処理するのに使うようです。 XML/Object変換時に気を付けたいこと結構はまるポイントがあります。XmlHttpParserのデフォルトのコンテンツタイプはapplication/xmlですが、これだと普通のtext/xmlを処理できません。 当然、text/plainで、帰ってきても処理できません。 NameSpace 必ず、指定すること、nullだとこういうエラーになります。 Exception in thread "main" java.lang.NullPointerException at com.google.api.client.xml.Xml.parseElementInternal(Xml.java:240) at com.google.api.client.xml.Xml.parseElement(Xml.java:203) at InputStreamParser.parse(InputStreamParser.java:64) さらに、XMLドキュメント側のnamespaceが、アプリ側のnamespaceに登録されていないと Exception in thread "main" java.lang.NullPointerException at com.google.api.client.xml.Xml.getFieldName(Xml.java:518) at com.google.api.client.xml.Xml.parseElementInternal(Xml.java:322) at com.google.api.client.xml.Xml.parseElement(Xml.java:203) at InputStreamParser.parse(InputStreamParser.java:64) at ParseTest2.main(ParseTest2.java:44) しかも、間違ってnamespace登録すると何もマッピングされません。 読み込むXMLにnamespace指定なければ、アプリ側も空にします。 public static XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); namespaceDictionary.set("", ""); 向こうが、<test xmlns="http://www.w3.org/TR/xml-names/"> とかですとアプリ側もおそろいにします。 public static XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); namespaceDictionary.set("", "http://www.w3.org/TR/xml-names/"); ファイルと違って、こちらだけxmlとかにするとうまく動きません。 namespaceDictionary.set("xml", "http://www.w3.org/TR/xml-names/"); ソケットで扱えないローカルのファイルをObjectに変換するでも、ローカルでUnitTestとかする時、不便な気がして(まあJettyとかあるし、テスト時にサーバー起動させればいいだけだけど)いろいろ試しました。 最初、Transportにfileのスキーム追加してと企てましたが、Socket通信でファイルやりとりとか、非現実的なのであきらめました。 結局、パーサーのXmlHttpParserを、XmlInputStreamParserとして、InputStreamを扱えるよう改良しました。 /* * Copyright (c) 2011 akjava.com * original is com.google.api.client.http.xml.XmlHttpParser; * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ /* * Copyright (c) 2010 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ import java.io.IOException; import java.io.InputStream; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import com.google.api.client.util.Types; import com.google.api.client.xml.Xml; import com.google.api.client.xml.XmlNamespaceDictionary; /** * XmlInputStreamParser is XmlHttpParser for File * @author aki * */ public class XmlInputStreamParser { private XmlNamespaceDictionary namespaceDictionary; public XmlInputStreamParser(XmlNamespaceDictionary namespaceDictionary) { this.namespaceDictionary=namespaceDictionary; } public <T> T parse(InputStream content, Class<T> dataClass) throws IOException { try { T result = Types.newInstance(dataClass); XmlPullParser parser = Xml.createParser(); parser.setInput(content, null); Xml.parseElement(parser, result, namespaceDictionary, null); return result; } catch (XmlPullParserException e) { IOException exception = new IOException(); exception.initCause(e); throw exception; } finally { content.close(); } } public final XmlNamespaceDictionary getNamespaceDictionary() { return namespaceDictionary; } } |
Google+ >