Android で XML と JSON を使用する: 第 1 回 JSON と XML が Android アプリケーションにもたらすメリットを探る

Android プラットフォームで使用するデータ交換フォーマットとして XML と JSON の両方を検討する

この全 2 回の連載記事では、インターネットで最もよく使われている 2 つのデータ・フォーマット、XML と JSON (JavaScript Object Notation) を Android プラットフォームで処理する場合の手法を探ります。第 1 回では XML と JSON の基本を説明した後、この 2 つのフォーマットで提供された Twitter のステータス更新フィードを構文解析して表示する Android アプリケーションの作成方法を紹介します。

Frank Ableson, Entrepreneur, MSI Services, Inc.

W. Frank Ableson は、妻の Nikki と子供たちと一緒にニュージャージー州北部に住む起業家です。モバイル・ソフトウェア、組み込み設計などを専門分野としています。彼は、『Unlocking Android』(Manning Publications、2010年) の著者であり、Linux Magazine のモバイル関連のエディターでもあります。



2010年 7月 20日

モバイル機器やモバイル・プラットフォームは、新たなリリースの度に今までにない機能や特色が追加されます。わずか数ヶ月後に、主要なモバイル・ベンダーから重大な発表があることも珍しくありません。その見出しは大抵の場合、UI に関する特色 (高度なマルチタッチ機能や Adobe® Flash® 技術など) であったり、ハードウェアの機能強化 (プロセッサー速度やストレージ容量など) であったりします。けれども、コンテンツが何よりも重要であることは動かしようのない事実です。コンテンツ (もっと一般的に言うと、データ) は、アプリケーション、サーバー、モバイル機器、そしてユーザーの間で絶えず交換されています。コンテンツを処理することができなければ、Apple の iPhone や Google の Android のようなスマートフォンは、単に価格が高いだけで、期待以下の携帯電話になり下がってしまいます。

よく使われる頭字語

  • API: Application Programming Interface
  • DOM: Document Object Model
  • HTML: HyperText Markup Language
  • IDE: Integrated Development Environment
  • SAX: Simple API for XML
  • SDK: Software Developer Kit
  • UI: User Interface
  • XML: Extensible Markup Language

Facebook や LinkedIn、そして Twitter などのソーシャル・ネットワーキング・プラットフォームが成し遂げた驚異的な成功を考えてみてください。純粋な特色や機能という観点で見ると、かなり平凡なものですが、ソーシャル・ネットワーキング・プラットフォームの人気は、メンバーやサイトの訪問者がそこに公開されているコンテンツに価値を見出しているところにあります。そしてそのコンテンツは、ますます多くのモバイル機器がアクセスするようになっています。

この記事では、Android プラットフォームで XML および JSON データ交換フォーマットを使用する方法を具体的に説明します。サンプル・アプリケーションに使用するデータのソースは、Twitter アカウントのステータス更新フィードです。フィード・データは、Twitter から XML フォーマットと JSON フォーマットの両方で取得することができます。この後わかるように、データを操作するためのプログラミング手法は、この 2 つのフォーマットではそれぞれに大きく異なります。

この記事に付属のサンプル・コードを実行するには、Android SDK バージョン 1.5 以降および Eclipse をインストールすることをお勧めします。環境のセットアップ方法についての詳細は、Android Developers Web サイトにアクセスして調べてください。また、必須ではありませんが、有効な Twitter アカウントを持っていると、サンプルを理解するのに役立つはずです。関連するリンクについては、「参考文献」を参照してください。

まずは、2 つのデータ・フォーマットの概要について、最初に XML、次に JSON の順で説明します。XML と JSON についてはすでに十分に理解しているというのであれば、「アプリケーションの機会: Twitter フィード」のセクションまで読み飛ばして、XML と JSON を Android で処理する手順を早速開始しても構いません。

XML: 旧知の友

過酷な作業はもうたくさんです

XML の自己記述的な性質は、XML が広範に採用されるようになる前の最新技術と比べてみると、その価値が明らかにわかります。かつてデータを交換するには、時間と労力を要するデータ記述文書を、大抵はワード・プロセッサーやスプレッドシート・アプリケーションを使って手作業で作成し、保守も手作業で行わなければなりませんでした。一般にインターフェース仕様として知られるこれらの文書が表現していたのは、フィールドの名前、長さ、区切り文字、階層などです。ユーザーが適当だと思って従っていた慣例の中で最も標準に近かったのは、お馴染みの CSV (Comma-Separated-Value) フォーマットですが、CSV ファイルでさえも、それぞれに大きく異なります。嘘だと思ったら、試しに CSV ファイルをスプレッドシート・プログラムにインポートして、すべてのオプションが使用できるかどうか調べてください。

この何年かの間に、エンタープライズや Web、あるいはモバイル市場を対象にプログラミングを行ったことがあれば、ほぼ間違いなく、XML を目にしているはずです。XML は至るところに溢れていると言っていいでしょう。

XML 文書には認識可能な構造があり、オプションで属性および子要素を含めることのできる一連の要素からなります。すべての有効な XML 文書は、先頭行の <?xml version="1.0" encoding="utf-8"?> 宣言で始まります。それに続く行はアプリケーションによって異なります。XML の長所は、読めば理解できるという点です。

XML スキーマ

XML 文書は自己記述的ではあるものの、特定のルールとガイドラインに従っていなければなりません。そこで登場するのが、XML スキーマです。XML スキーマとは、特定の XML ファイルの構造を記述する文書のことです。XML ファイルの構造が冗長で、複雑であることは珍しくありません (XML が IT の分野にもたらした最悪の事態はほぼ間違いなく、データの急増です。極めて記述的なデータ構造の概念が流行し始めたことがその原因で、さらに過去 10 年間、ディスク・ストレージ技術のコストが激減したことによっても拍車がかかりました)。

XML 文書のように大きくて複雑なファイルが当たり前のことになるにつれ、手作業によるファイル処理は、プログラマーやアナリストにとって役立たずの手法となってきました。この問題に対処するために出回るようになってきたのが、ファイルの管理とファイルに関わるタスク (文書化やレガシー・フォーマットへの変換など) を支援する XML エディターと妥当性検証ツールです。

一般的なテキスト・データの他、XML ではバイナリー・データを保管することもできます。その手段としては、CDATA として知られる特殊なタグのセットが使用されます。XML 文書内の CDATA タグには、他のマークアップ・テキストを始めとするあらゆる種類のデータを含めることが可能です (ただし、テキスト・データ自体に CDATA が含まれていない場合)。

API が各種のデータを含めるために、リクエスト/レスポンス・クエリーを実行するための構造として XML を使うことは珍しくありません。レスポンス・データに XML 構造が含まれ、その構造が CDATA タグ内に収まっていることはよくあります。例えば API 呼び出しで、Mott という苗字を持つ顧客のレコードをリクエストするとします。データが見つかると、そのデータは XML 構造に取り込まれた上で、response 要素内に配置されます (リスト 1 を参照)。

リスト 1. XML 構造に取り込まれて response 要素内に配置されたデータ
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<request>
<query>
<lastname>Mott</lastname>
<maxhits>100</maxhits>
</query>
</request>

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<response>
<returncode>200</returncode>
<query>
<lastname>Mott</lastname>
<hits>1</hits>
</query>
<data>
<![CDATA[
<contact>
<firstname>Troy</firstname>
<lastname>Mott</lastname>
<age>not telling</age>
</contact>
]]>
</data>
</response>

XML が活躍する場

現在、XML は想定されるデフォルトのデータ・フォーマットとなっています。同じデータを XML 以外のフォーマットでも使用できるとしても、XML 構造で使用できることを前提とするのが賢明です。

ERP (Enterprise Resource Planning) パッケージでは、データのインポート/エクスポート・タスクに XML を頻繁に使用します。インターネット・ニュース・サイトではよく、データを RSS (Really Simple Syndication) フィードとして利用できるようにしていますが、RSS フィードもまた、ニュース・リーダーによって処理されるようにあらかじめフォーマットが定義された XML 文書です。さらに、OpenOffice.org や Microsoft® Office などの文書作成アプリケーションでも XML を使用します。

最近の Microsoft Office 文書は、複数の XML 文書が含まれる PKZIP 互換ファイルとなっています。すべての XML ファイルには、先頭行に共通の宣言があります。リスト 2 からわかるように、その属性については多少理解しづらいかもしれません。

リスト 2. 各 XML ファイルの先頭行にある共通の宣言
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <w:document xmlns:ve="http://schemas.openxmlformats.org/markup-compatibility/2006"
 xmlns:o="urn:schemas-microsoft-com:office:office"
 xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
 xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math"
 xmlns:v="urn:schemas-microsoft-com:vml"
 xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing"
 xmlns:w10="urn:schemas-microsoft-com:office:word"
 xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
 xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml">
 <w:body><w:p w:rsidR="00B6337C" w:rsidRDefault="00663F0E"><w:r>
 <w:t xml:space="preserve">This is a sample </w:t></w:r><w:r
 w:rsidRPr="006906EA"><w:rPr><w:i/></w:rPr><w:t>Microsoft 
 Word document</w:t></w:r><w:r><w:t xml:space="preserve"> used
 to </w:t></w:r><w:r w:rsidRPr="006906EA"><w:rPr><w:b/>
 <w:u w:val="single"/></w:rPr><w:t>demonstrate</w:t></w:r>
 <w:r><w:t xml:space="preserve"> some XML topics.</w:t></w:r>
 </w:p><w:p w:rsidR="00B14B2A" w:rsidRDefault="00B14B2A"/><w:p 
 w:rsidR="00B14B2A"w:rsidRDefault="00B14B2A"><w:r><w:rPr>
 <w:noProof/></w:rPr><w:drawing><wp:inline distT="0" distB="0" 
 distL="0" distR="0"><wp:extent cx="3276600" cy="3838575"/><wp:effectExtent
 l="19050" t="0" r="0" b="0"/><wp:docPr id="1" name="Picture 0"
 descr="frankableson.jpg"/><wp:cNvGraphicFramePr><a:graphicFrameLocks
 xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"
 noChangeAspect="1"/></wp:cNvGraphicFramePr><a:graphic
 xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"><a:graphicData
 uri="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:pic
 xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture">
 <pic:nvPicPr><pic:cNvPrid="0"name="frankableson.jpg"/><pic:cNvPicPr/>
 </pic:nvPicPr><pic:blipFill><a:blip r:embed="rId4"
 cstate="print"/><a:stretch><a:fillRect/></a:stretch>
 </pic:blipFill><pic:spPr><a:xfrm><a:off x="0" y="0"/>
 <a:ext cx="3276600" cy="3838575"/></a:xfrm><a:prstGeom
 prst="rect"><a:avLst/></a:prstGeom></pic:spPr></pic:pic>
 </a:graphicData></a:graphic></wp:inline></w:drawing>
 </w:r></w:p><w:p w:rsidR="00663F0E" w:rsidRDefault="00663F0E"/>
 <w:p w:rsidR="00CC16CE" w:rsidRDefault="00CC16CE"/><w:sectPr 
 w:rsidR="00CC16CE" w:rsidSect="00B6337C"><w:pgSz w:w="12240" w:h="15840"/>
 <w:pgMar w:top="1440" w:right="1440" w:bottom="1440" w:left="1440" w:header="720" 
 w:footer="720" w:gutter="0"/><w:cols w:space="720"/><w:docGrid
 w:linePitch="360"/></w:sectPr></w:body></w:document>

XML は自己記述的ですが、だからと言って必ずしも、簡単にタグを解釈できるわけではありません。上記の不可解な例では、XML 名前空間が複数使用されているため、特殊なツールを使わなければ、XML 文書を理解するのはより一層難しくなっています。

XML は至るところで使われていますが、Android プログラマーにとって XML があまり望ましい選択とは言えないことはよくあります。特に、データの構造が XML 構造に伴いがちなデータ急増の犠牲になってしまうとしたら、XML を選択すべきではありません。セルラー・データ・ネットワークで通常動作する Android のようにリソースに限りのあるプラットフォームでは、大量の XML データを保管して構文解析することができないためです。けれども、特定のプログラミング・タスクでテキスト・データとバイナリー・データの両方を交換しなければならないとしたら、XML が堅実な選択肢となります。

今度は、XML の代わりとなるデータ交換フォーマット、JSON について見ていきます。


JSON: インターネットの新顔

JSON をデータ・フォーマットの選択肢として提供するインターネット API プロバイダーは増え続けています。JSON が一躍有名になった舞台は、Ajax (Asynchronous JavaScript and XML) Web プログラミングのコミュニティーです。Ajax 技術は、ページ全体ではなく、選択された部分のデータを最新にすることによって、Web ページを動的に更新できるようにします。転送されるデータが少なくなること、さらに重要な点として、構文解析してブラウザーのウィンドウに描画するデータが大幅に減ることから、Ajax 対応のアプリケーションは従来の Web アプリケーションよりも遥かに優れたユーザー・エクスペリエンスを提供することができます。実際、上手く作成された Ajax アプリケーションは、ユーザー・エクスペリエンスという点でスマート・クライアント・アプリケーションやファット・クライアント・アプリケーションの好敵手となり得ます。

Ajax アプリケーションが Web サーバーとデータを交換する場合には、何らかのデータを最新のものにするようにリクエストしているケースが多いですが、その際フォーマットの指定がないのが理想的です。一般に、Web サーバーがフォーマットを指定して HTML を提供するのは不適切なプラクティスと見なされます。適切に作成されたアプリケーションであれば、データ・コンテンツをブラウザーに送信し、CSS (Cascading Style Sheets) ファイルを適用することによって、色やフォントの詳細を指定するなどの視覚的な効果をもたらすようにするべきです。

例えば、あるアプリケーションで、架空の人物 Mr. Mott の連絡先レコードをリクエストする必要があるとします。このアプリケーションがブラウザーに返すデータ要素は複数あります。その場合、データ要素はどのようにパッケージ化されると思いますか?リスト 1 の例に記載した単純な XML のリクエスト/レスポンス構造を使用するのでも全く問題はありませんが、そうなると、サーバーからの各レスポンスを構文解析し、データをある種の構造 (DOM) に保管してから、Web ページのコンテンツを更新しなければならなくなります。

別の方法として考えられるのは、サーバーから JavaScript を取得し、その JavaScript を直接処理するという単純な方法です。以下に、Mott という人物に対するクエリー (http://<yourserver/app/searchcontact?Mott) に対して、仮想的なアプリケーションが返すレスポンスの例を記載します。このレスポンスは、JavaScript オブジェクトのストリング表現、すなわち JSON ストリングです (ここでは記事のページ幅に合わせるために 2 行に分割して記載します)。

[{"firstname":"Troy","lastname":"Mott","age":"don't ask!"},{"firstname":"Apple seed",
   "lastname":"Mott's","age":"99"}]

XML がその冗長さで知られている一方、JSON は若干読みにくいと言われています。JSON オブジェクトは、キーと値のペア ([キー].[値]) というフォーマットで構成されます。オブジェクトの要素はそれぞれカンマで区切られ、各オブジェクトは波括弧 { } で、オブジェクトの配列は角括弧 [ ] で囲まれます。一般的な手法では、オブジェクトの配列にデータベースの一連の行が転送されます。この場合、各配列要素がデータベースの行に対応し、オブジェクトの各プロパティーがデータの列を表します。

リスト 3 に、このようなオブジェクトを HTML ページ内で使用する例を記載します。簡潔にするため、サーバーとの通信は省略し、代わりに serverresponse という名前のストリング変数として提供された JSON データを記載しています。

リスト 3. HTML ページ内での JSON オブジェクトの使用
<html>
<head>
<script language="JavaScript">
var serverresponse = "[{\"firstname\":\"Troy\",\"lastname\":\"Mott\",\"age\":\"don't
ask!\"},{\"firstname\":\"Apple seed\",\"lastname\":\"Mott's\",\"age\":\"99\"}]";
function updatepage()
{
    var contacts = eval(serverresponse );
    var i;
    var s = "Search Results:<br />";
    for (i=0;i<contacts.length;i++)
    {
        s = s + contacts[i].firstname + " " + contacts[i].lastname + "'s age is ... " 
+ contacts[i].age + "<br />";
    }
    document.getElementById("target").innerHTML = s;
}
</script>
</head>
<body>
<button onclick="updatepage();">Search for Mott</button><br />
<span id="target">&nbsp;</span>
</body>
</html>

上記の例では、eval() という JavaScript 関数を使用して、ストリングを JavaScript の配列に変換していますが、このステップをより素早く、よりセキュアに行うメソッドを提供する JSON ライブラリーもあります。リスト 3 の手法は、ベスト・プラクティスではありません。この手法を記載したのは、Ajax アプリケーションでは JSON オブジェクトをどのような状況で使用できるのかを示すためです。その状況とは、クライアントのコードで JSON 構造を交換し、構文解析して操作するような状況です。

JSON は以下のように要約することができます。

  • データ交換フォーマットです。
  • JavaScript オブジェクトをストリングとしてエンコードする手段です。
  • 使用できるのはテキストおよび数値に限られます。バイナリー値は明らかに使用できません。JSON には CDATA に相当するものがないからです。
  • データ・サイズという点では、XML よりも効率的ですが、読み易さが犠牲になります。
  • Twitter をはじめ、データを提供する際の選択肢として選択可能にする API プロバイダーが多くなってきています。

リスト 3 のクライアントは、クライアント・サイドのスクリプトを実行する Web ブラウザーです。次のセクションでは、話題をこの記事の焦点に戻し、Android アプリケーションで XML および JSON を使用する場合について検討します。


アプリケーションの機会: Twitter フィード

世界的勢力を持つようになった Twitter は、例えば朝食に何を食べているか、子供の野球チームの試合の状況はどうであるかといった話題から、近隣諸国で政治的反乱が起きているときの街の様子、臓器移植の実況といった深刻な話題まで、あらゆる最新情報を提供しています。

この記事に付属のサンプル・コードで使用する XML 文書と JSON 文書を取得するのに最も簡単な方法は、URL http://twitter.com/statuses/user_timeline/userid.format にアクセスすることです。ここで、userid には自分の Twitter のユーザー名を指定し、format には XML または JSON を指定してください。

フィード・ページへのリンクは、Twitter ページにも表示されています (図 1 を参照)。ユーザーそれぞれの Twitter ページでは、Twitter のユーザー名も確認することができます。

図 1. Twitter ページに表示されたフィード・ページへのリンク
fableson の Twitter ページのスクリーン・キャプチャー。右下隅に、「RSS feed of fableson's tweets (fableson のツイートへの RSS フィード)」リンクが表示されています。

完全なフィード・ファイルはかなり冗長なので、次に記載する 2 つのリストには、(私自身の Twitter アカウントからの) フィードの最初のエントリーだけを抜粋します。まずリスト 4 に、XML のスニペットを記載します。

リスト 4. XML のスニペット
<?xml version="1.0" encoding="UTF-8"?>
<statuses type="array">
<status>
  <created_at>Thu Apr 29 05:25:29 +0000 2010</created_at>
  <id>13052369631</id>
  <text>Wrapping up new article on JSON for Android
 programmers...</text>
  <source><a href="http://www.linkedin.com/"rel="nofollow">
   LinkedIn</a></source>
  <truncated>false</truncated>
  <in_reply_to_status_id/>
  <in_reply_to_user_id/>
  <favorited>false</favorited>
  <in_reply_to_screen_name/>
  <user>
    <id>15221439</id>
    <name>fableson</name>
    <screen_name>fableson</screen_name>
    <location>Byram Township, NJ</location>
    <description/>

<profile_image_url>http://a3.twimg.com/profile_images/260492935
/bookcover_normal.jpg</profile_image_url>
    <url>http://msiservices.com</url>
    <protected>false</protected>
    <followers_count>52</followers_count>
    <profile_background_color>9ae4e8
    <profile_text_color>000000</profile_text_color>
    <profile_link_color>0000ff</profile_link_color>
    <profile_sidebar_fill_color>e0ff92
</profile_sidebar_fill_color>
    <profile_sidebar_border_color>87bc44
</profile_sidebar_border_color>
    <friends_count>10</friends_count>
    <created_at>Tue Jun 24 17:04:11 +0000 2008</created_at>
    <favourites_count>0</favourites_count>
    <utc_offset>-18000</utc_offset>
    <time_zone>Eastern Time (US & Canada)</time_zone>

   <profile_background_image_url>http://s.twimg.com/a/1272044617/
images/themes/theme1/bg.png</profile_background_image_url>
   
<profile_background_tile>false</profile_background_tile>
    <notifications>false</notifications>
    <geo_enabled>false</geo_enabled>

    <verified>false</verified>
    <following>false</following>
    <statuses_count>91</statuses_count>
    <lang>en</lang>
    <contributors_enabled>false</contributors_enabled>
  </user>
  <geo/>
  <coordinates/>
  <place/>
  <contributors/>
</status>
</statuses>

リスト 5 に記載するのは上記と同じデータですが、フォーマットは JSON です。

リスト 5. JSON フォーマットのフィード・データ
[
{"in_reply_to_status_id":null,
"favorited":false,
"created_at":"Thu Apr 29 05:25:29 +0000 2010",
"in_reply_to_screen_name":null,
"geo":null,
"source":"<a href=\"http://www.linkedin.com/\" rel=\"nofollow\
          ">LinkedIn</a>",
"contributors":null,
"place":null,
"truncated":false,
"coordinates":null,
"user":
{
    "friends_count":10,
    "description":"",
    "lang":"en",
    "statuses_count":91,
    "time_zone":"Eastern Time (US & Canada)",
    "profile_link_color":"0000ff",
    "favourites_count":0,
    "created_at":"Tue Jun 24 17:04:11 +0000 2008",
    "contributors_enabled":false,
    "profile_sidebar_fill_color":"e0ff92",
    "following":null,
    "geo_enabled":false,
    "profile_background_image_url":"http://s.twimg.com/a/1272044617/images/themes
/theme1/bg.png",
    "profile_image_url":"http://a3.twimg.com/profile_images/260492935
/bookcover_normal.jpg",
    "notifications":null,
    "profile_sidebar_border_color":"87bc44",
    "url":"http://msiservices.com",
    "verified":false,
    "profile_background_tile":false,
    "screen_name":"fableson",
    "protected":false,
    "location":"Byram Township, NJ",
    "profile_background_color":"9ae4e8",
    "name":"fableson",
    "followers_count":52,
    "id":15221439,
    "utc_offset":-18000,
    "profile_text_color":"000000"
},
"in_reply_to_user_id":null,
"id":13052369631,
"text":"Wrapping up new article on JSON for Android programmers..."}
]

いずれのリストにしても、単純なステータス更新の他に大量のデータが含まれていますが、ここで対象とする部分は、投稿が行われた日付と時刻、そして投稿の内容そのもののテキストだけです。次のセクションでは、Android アプリケーションのなかで、このデータの構文解析に関連する部分を説明します。完全なプロジェクトについては、ダウンロードから入手してください。


XMLvsJSON アプリケーション

実際のリアルタイムの更新

このサンプル・アプリケーションは、実際のアプリケーションに期待されるように、Web からリアルタイムでデータを取得するのではないことに注意してください。サンプル・アプリケーションでは構文解析の側面に集中できるよう、raw リソース・フォルダーに置かれたデータ・フィードを取得します。Android でネットワークに接続する方法に関しては、「参考文献」のリンクを参照してください。

この単純な Android アプリケーションには、XML データ・フィードと JSON データ・フィードの完全なコピーが含まれていて、ユーザーはいずれかのデータ・フィードを選択して構文解析することができます。Eclipse 内では、このプロジェクトの構成は図 2 のようになっています (図 2 をテキストで表現したものを見る)。

図 2. Eclipse プロジェクトのファイル構成
Eclipse 内でのファイル構成のスクリーン・キャプチャー
Eclipse 内でのファイル構成のテキスト版 (図 2)
v XMLvsJSON
  > src
  > gen [Generated Java Files]
  > Android 2.1
    assets
  v res
    > drawable-hdpi
    > drawable-ldpi
    > drawable-mdpi
    > layout
    > raw
        jsontwitter.txt
        xmltwitter.txt
    > values
    AndroidManifest.xml
    default.properties

図 3 に、構文解析オプションを選択する前のアプリケーションの UI を示します。

図 3. 構文解析オプションを選択する前のアプリケーションの UI
構文解析オプションを選択する前のアプリケーションのユーザー・インターフェースのスクリーン・キャプチャー

アプリケーションの UI には、「Parse XML (XML の構文解析)」と「Parse JSON file (JSON ファイルの構文解析)」という 2 つのボタンと、その下にデフォルト・テキストが表示されます。この UI のレイアウトは、リスト 6 に記載するとおりです。このリスト 6 を含む main.xml ファイルは、プロジェクトの res/layout フォルダーに置かれています。

リスト 6. UI のレイアウト
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:gravity="center_horizontal">

<Button android:layout_width="wrap_content" android:layout_height="wrap_content"
 android:id="@+id/btnXML" android:text="Parse XML"></Button>
<Button android:layout_width="wrap_content" android:layout_height="wrap_content"
 android:id="@+id/btnJSON" android:text="Parse JSON file"></Button>
</LinearLayout>

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" 
android:id="@+id/ScrollView01" android:layout_width="fill_parent"
android:layout_height="wrap_content">

<TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="default text" 
    android:layout_gravity="center_horizontal"
    android:id="@+id/txtData" 
    />


</ScrollView>

</LinearLayout>

Parse XML (XML の構文解析)」および「Parse JSON file (JSON ファイルの構文解析)」ボタンの定義の下に、TextView コントロールが含まれる ScrollView が定義されています。これは、構文解析されたデータをユーザーがスクロールできるようにするためです。

複数の LinearLayout 構造が使用されていることに注意してください。最初の LinearLayout は垂直方向の位置合わせで、この構造には垂直構造の LinearLayout、そして ScrollView の両方が含まれています。内側の LinearLayout には、2 つの Button ウィジェットがあります。このレイアウトは拡張されて、onCreate() メソッド内で使用されます (リスト 7 を参照)。

リスト 7. onCreate() メソッド
    Button btnXML;
    Button btnJSON;
    TextView tvData;
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);


        tvData = (TextView) findViewById(R.id.txtData);
        btnXML = (Button) findViewById(R.id.btnXML);
        btnXML.setOnClickListener(new Button.OnClickListener()
        {
            public void onClick(View v)
            { 
                examineXMLFile();
            }
        });


        btnJSON = (Button) findViewById(R.id.btnJSON);
        btnJSON.setOnClickListener(new Button.OnClickListener()
        {
            public void onClick(View v)
            {
                examineJSONFile();
            }
        });

    }

上記に示されている examineXMLFile() メソッドが、XML の構文解析を制御します。


XML の構文解析

SAX と DOM の違い

Android は DOM パーサーもサポートしています。DOM パーサーは比較的大きなメモリー・フットプリントとなりますが、SAX パーサーに見られる複雑さは軽減されます。XMLvsJSON のようなアプリケーションでは、大きなデータ・フィードのほんのわずかなサブセットしか対象としないため、SAX パーサーのほうがより適した構文解析ツールとなるはずです。

XML データの構文解析には、よく SAX スタイルのパーサーが使用されます。このスタイルのパーサーでは、ソース XML データを指す InputSource をセットアップし、文書の「探索」中に特定のイベントを受け取るハンドラーを提供します。リスト 8 に記載する examineXMLFile() メソッドは、以下のタスクを行います。

  • raw リソース・フォルダー内の XML ファイルを指定した InputSource をセットアップします。
  • SAXParser を作成し、twitterFeedHandler という名前のハンドラー (リスト 9 を参照) と関連付けます。
  • パーサーを呼び出し、レイアウト・ファイルでは R.id.txtData として識別され、コード内では tvData として参照されている TextView ウィジェットに結果を表示します。
  • エラーがある場合には、同じく TextView に表示します。
リスト 8. examineXMLFIle() メソッド
void examineXMLFile()
    {
        try {
            InputSource is = new InputSource(getResources()
.openRawResource(R.raw.xmltwitter));
            // create the factory
            SAXParserFactory factory = SAXParserFactory.newInstance();
            // create a parser
            SAXParser parser = factory.newSAXParser();
            // create the reader (scanner)
            XMLReader xmlreader = parser.getXMLReader();
            // instantiate our handler
            twitterFeedHandler tfh = new twitterFeedHandler();

            // assign our handler
            xmlreader.setContentHandler(tfh);
            // perform the synchronous parse
            xmlreader.parse(is);
            // should be done... let's display our results
            tvData.setText(tfh.getResults());
        }
        catch (Exception e) {
            tvData.setText(e.getMessage());
        }
    }

examineXMLFile() が構文解析のお膳立てをする一方、アプリケーションの観点から見た実際の構文解析は、twitterFeedHandler.java ファイルに実装されたハンドラーで行われます。DefaultHandler インターフェースを実装するこのクラスを、リスト 9 に記載します。

リスト 9. twitterFeedHandler クラス
public class twitterFeedHandler extends DefaultHandler {

    StringBuilder sb = null;
    String ret = "";
    boolean bStore = false;
    int howMany = 0;

    twitterFeedHandler() {
    }

    String getResults()
    {
        return "XML parsed data.\nThere are [" + howMany + "] status updates\n\n" + ret;
    }
    @Override

    public void startDocument() throws SAXException {
        // initialize "list"
    }

    @Override
    public void endDocument() throws SAXException {

    }

    @Override
    public void startElement(String namespaceURI, String localName, String qName, 
Attributes atts) throws SAXException {

        try {
            if (localName.equals("status")) {
                this.sb = new StringBuilder("");
                bStore = true;
            }
            if (localName.equals("user")) {
                bStore = false;
            }
            if (localName.equals("text")) {
                this.sb = new StringBuilder("");
            }
            if (localName.equals("created_at")) {
                this.sb = new StringBuilder("");
            }
        } catch (Exception ee) {

            Log.d("error in startElement", ee.getStackTrace().toString());
        }
    }

    @Override

    public void endElement(String namespaceURI, String localName, String qName)
throws SAXException {

        if (bStore) {
            if (localName.equals("created_at")) {

                ret += "Date: " + sb.toString() + "\n"; 
                sb = new StringBuilder("");
                return;

            }

            if (localName.equals("user")) {
                bStore = true;
            }

            if (localName.equals("text")) {

                ret += "Post: " + sb.toString() + "\n\n";
                sb = new StringBuilder("");
                return;

            }


        }
        if (localName.equals("status")) {
            howMany++;
            bStore = false;
        }
    }

    @Override

    public void characters(char ch[], int start, int length) {

        if (bStore) {
            String theString = new String(ch, start, length);

            this.sb.append(theString);
        }
    }

}

リスト 9 には、注目に値する項目がいくつかあります。まず 1 つは、SAX パーサーがイベント・ベースのパーサーであることです。これは、構文解析と並行して実際の文書を作成することを意味します。文書の開始時、文書の終了時、タグの開始時、タグの終了時、そしてデータの検出時にはいずれもイベントが起動されます。これはつまり、対象のデータを維持するデータ構造を定義し、対象以外のデータを破棄する必要があるということです。

StringBuilder、そしてデータを追加していることにも注目してください。これは、特定のデータ要素は、InputSource での複数の読み取り操作で処理される場合があることから使用されています。データのすべてがある特定の characters() メソッド呼び出しで提供されるという前提にすることは禁物です。

このアプリケーションは、データを収集して単純なフォーマットが設定された 1 つのストリングにまとめますが、例えば構文解析後に大量の処理が行われる場合をはじめ、これらのエントリーをコレクション型のクラスやデータベースに統合する場合も考えられます。

getResults() メソッドはこのクラスのカスタム・メソッドで、データの表現を 1 つに組み立ててアプリケーションに渡すために使用されます。このメソッドは DefaultHandler のインターフェースには含まれません。

図 4 に、構文解析された XML データを示します (図 4 をテキストで表現したものを見る)。

図 4. 構文解析後の XML データ
携帯電話の画面に表示された構文解析後の XML データのスクリーン・キャプチャー
携帯電話の画面に表示された構文解析後の XML データのテキスト版 (図 4)
XMLvsJSON
      [Parse XML]  [Parse JSON file]
XML parsed data.
There are [20] status updates.

Date: Thu Apr 29 05:25:29 +0000 2010
Post: Wrapping up new articles on JSON for
Android programmers...

Date: Thu Apr 27 16:39:10 +0000 2010
Post: taking photos with Android's built-in camera.
And a hidden (but easy) puzzle. http://lnkd.in/
cbwEGF

Date: Thu Apr 22 20:20:23 +0000 2010
Post: Launched beta version of iPhone app today
for a client.

Date: Thu Apr 19 15:53:13 +0000 2010
Post: There is a worm in Apple's iOS 4.0 http://
lnked.in6ajf3m

Date: Thu Apr 17 23:20:48 +0000 2010
Post: Interesting development in iPad land -- 
don't bring it to Israel. http://lnkd.in/b7VPxR

SAX パーサーによる XML の構文解析は、構造の組み立て、管理、ナビゲーションという点では簡単ではありませんが、速度に優れていること、そして構文解析中、構文解析後に必要な RAM の量を大幅に削減できる可能性があることが、その主な利点です。

次は、Android で JSON データを構文解析する場合を説明します。


JSON の構文解析

アプリケーションでの JSON データの構文解析は、ユーザーが JSON ボタンを選択した時点で始まります。このボタンが選択されると、examineJSONFile() メソッドが呼び出されます (リスト 10 を参照)。JSON の構文解析には、ハンドラー・クラスは必要ありません。なぜなら、この場合の構文解析と文書管理はすべて、Android が提供するライブラリーの中で行われ、すべての JSON 関連のコードは、このメソッドに含まれているからです。

リスト 10. examineJSONfile() メソッドの呼び出し
void examineJSONFile()
    {
        try
        {
            String x = "";
            InputStream is = this.getResources().openRawResource(R.raw.jsontwitter);
            byte [] buffer = new byte[is.available()];
            while (is.read(buffer) != -1);
            String jsontext = new String(buffer);
            JSONArray entries = new JSONArray(jsontext);

            x = "JSON parsed.\nThere are [" + entries.length() + "]\n\n";

            int i;
            for (i=0;i<entries.length();i++)
            {
                JSONObject post = entries.getJSONObject(i);
                x += "------------\n";
                x += "Date:" + post.getString("created_at") + "\n";
                x += "Post:" + post.getString("text") + "\n\n";
            }
            tvData.setText(x);
        }
        catch (Exception je)
        {
            tvData.setText("Error w/file: " + je.getMessage());
        }
    }

前のセクションで説明した XML のルーチンと同じく、このコードは raw リソース・フォルダーにあるファイルを読み込みます。データはそっくりそのままメモリー内に読み込まれ、java.lang.String に変換されてから JSONArray に構文解析されます。注目すべき点は、特定のストリングは、この例でのようにそのまま配列に構文解析できることです。それ以外のストリングは、JSONObject に構文解析することができます。Twitter データはオブジェクトの配列であるため、ストリング全体を配列に構文解析してから、順序位置を基準に個別のオブジェクトにアクセスするという方法は理にかなっています。

このメソッドのフローは至って単純です。データが構文解析されると、コードがストリング表現を組み立てます。これは、XML パーサー・ハンドラーの手法と似ていますが、JSON で興味深い点は、データが自動的に管理されることです。追加のメモリー構造を作成してデータを保管する必要はありません。同様に、アプリケーションは JSONArray に含まれるエントリーの数を前もって把握します (この例では 20)。

JSON の構文解析はプログラムの点から見ると極めて単純ですが、犠牲が伴わないわけではありません。データを処理するには、あらかじめデータ・ストリーム全体を読み込まなければならないこと、そしてデータのすべてを保管することから、メモリー使用量に関する負担が増大します。SAX XML の手法では、それとは対照的に、対象となるデータしか使用しません。けれどもメモリーの負担は別として、特定の JSON オブジェクトを構文解析するためのメモリーが十分にあるとしたら、特に DOM の処理にはほとんど興味がない場合をはじめ、この手法のほうが多くのアプリケーションにとって望ましい方法となるはずです。


まとめ

この記事では、Android アプリケーションというコンテキストで、XML および JSON データ交換フォーマットを紹介しました。JSON の手法に比べ、XML の手法は多少速度に優れ、メモリーの制約も少なくなりますが、それには複雑さが増すという犠牲が伴います。第 2 回では、Android アプリケーションに JSON データ、WebKit ベースの WebView ウィジェット、そしてカスタム動的アプリケーション・ロジックを組み合わせるという高度な手法を紹介します。


ダウンロード

内容ファイル名サイズ
Article source codexmlvsjosn.zip68KB

参考文献

学ぶために

製品や技術を入手するために

  • Android SDK: Android SDK をダウンロードしてください。この記事の演習には、バージョン 1.5 以降を使用することができます。
  • Eclipse: 最新の Eclipse IDE を入手してください。
  • Android Open Source Project: Android はオープンソースなので、ここからソース・コードを入手できます。
  • JSONLint: このオンライン JSON バリデーターを試してみてください。
  • IBM 製品の評価版: DB2®、Lotus®、Rational®、Tivoli®、および WebSphere® のアプリケーション開発ツールとミドルウェア製品を体験するには、評価版をダウンロードするか、IBM SOA Sandbox のオンライン試用版を試してみてください。

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=XML, Web development
ArticleID=513017
ArticleTitle=Android で XML と JSON を使用する: 第 1 回 JSON と XML が Android アプリケーションにもたらすメリットを探る
publish-date=07202010