Neutral Scent

App developments & Gadgets

Web APIで取得したXML/JSONからVSデザイナで表示できるリアルなXAMLサンプルデータを作成する


どうもこんにちは、手書きXAML派、id:kaorunです。
このエントリーはXAML Advent Calendar 2013、2日目のエントリーです。
今回は、各種Web APIから取得したXML/JSONデータをサンプルデータとしてXAMLに差し込むおおまかな方法をご紹介します。
手書きXAML派でもデザイナーさんとは仲良くしたいよね、やっぱりきちんとリアルデータでプレビューしたいよね、と最近ちょっと研究していた点をまとめてみました。既に実践されてた方々には何をいまさらという話です。
一口にWeb APIといっても要求に対する応答形式は千差万別ですのでさくっとできるとは限りませんが、逆にXMLJSONであればWeb APIでなくても通用する手法かと思います。
このエントリーは予備知識として、XAMLの基本的な文法、ListBoxのItemTemplateによるデザインやDataBindingに関する知識があるという前提で書かれています。
ItemTemplateに関してはDay1のぐらばくさんが非常に素晴らしいエントリーを書いてくださったので、ListBoxのテンプレート定義とかなんだかいまいちよくわからん、という方はぜひご一読を!

要約:

  • API応答のXML/JSONデータを取得する
    • ブラウザ等で応答の生データを取得する
    • テキストでクリップボードへコピー
  • 応答データ格納クラスを作成
    • 新規クラスファイル(*.cs)作成
    • Visual Studioで「形式を選択して貼り付け」/「XML(JSON) をクラスとして貼り付ける」
  • サンプルデータの生成
    • Blendでプロジェクトを開く
    • データウインドウ /「サンプルデータの作成」/「クラスからサンプルデータの作成...」
    • 表示対象となるListBox等に作成されたサンプルデータをDrag&Drop
    • Visual Studioに戻って、サンプルデータの調整(API応答生データを差し込み)
  • VSデザイナでコントロールのデザイン

では、順を追って手順を見ていきましょう。
1. API応答のXML/JSONデータを取得する
まずは、API応答の生データを取得します。
とりあえず、受け皿になる新規プロジェクトとしてWindowsストアアプリでも作ってみましょうか。
基本的にWindowsストアアプリ、WPFWindows PhoneなどXAML系プラットフォームではほぼ同様の手続きが可能のはずです。

定番の例として、はてなブックマークのhotentryをRSSで取得してみましょう。
ブラウザで生RSSを取得します。
http://b.hatena.ne.jp/hotentry.rss

(IEだと、既定ではXMLが来たときDOMツリー表示してしまうので、この結果をそのままコピペしてしまうとNGです。「ソースの表示」や「ファイル」/「名前を付けて保存」で生のXMLデータとして取得してください)
で、この生のXMLデータを全選択して、クリップボードへコピーします。
ようするに、Web APIの応答してきたXMLデータをテキストとしてクリップボードに突っ込むことが重要。応答がJSONの場合も全く同様です。
単純にブラウザでGETできない認証が必要なAPI等の場合は、デバッガでひっかけてResponseを取得するなり、Fiddlerで横取りするなりしましょう。

2. 応答データ格納クラスを作成

さて、Web APIにはそれぞれ定義されたスキーマに基づいたXMLJSONデータを応答してくるのが通例です。
つまり、このデータを格納するクラスもまた作成可能、なはずですね。
そこで、Visual Studioの強力機能のひとつ「XML(JSON) をクラスとして貼り付ける」を使用します。
この機能の利用環境など、詳しくはこちらのしばやんのエントリーで、(VS2013では標準で使えるんじゃないか、と)

Visual Studio 2012 の「JSON / XML をクラスとして貼り付ける」機能が便利すぎるけど - しばやん雑記
http://shiba-yan.hatenablog.jp/entry/20130711/1373549632

ようするに、新規クラスファイル(*.cs)を作って、「形式を選択して貼り付け」/「XML(JSON) をクラスとして貼り付ける」を実行するだけです。
今回はSampleModelフォルダを作って、その下にHatenaRSS.csを作成しています。


どーん、とスキーマ実装クラスが作成されました。

今回のエントリーの目的は、実際にデータ処理をする部分ではないので、厳密に応答データと一致して処理できるクラスになっている必要はありません。
極論、データバインディングでBindされるpath名が一致していれば実際の実行時に表示されるクラスと型が一致している必要もないため、まずは上記のようにサンプル用のフォルダを切って名前空間を分けるなどしておくが一つの手ではないかと思います。

3. サンプルデータの生成

次はBlendを使用して仮のサンプルデータを作成します。
手書きXAML派としては、Blendではなく自分で手書きで、と思わなくもないですがそのあたりは後述。
ここまでの状態で、VSで一回ビルドします。これ重要。ビルドしないとBlendがクラスを認識してくれません。
それでは、編集中のプロジェクトをBlendで開きましょう。Windowsストアアプリならエクスプローラーからプロジェクトを右クリックして「Blendで開く」でBlendを起動します。その他のプラットフォームのプロジェクトでメニューが出ない場合は、別途Blendを起動してslnなりcsprojなりをプロジェクトとして開けばok。

既定値の状態のBlendなら、ウインドウ右上にデータウインドウのタブが見えていると思うのでこれを選択。見当たらない場合はメニューから、ウインドウ/データウインドウで表示させて探します。

データウインドウさらに右上にある小さいアイコン「サンプル データの作成」をクリックしてプルダウン、「クラスからサンプル データの作成」を選択します。

サンプルデータを作成するクラスの選択ダイアログが表示されるので、この場合RSS応答のルート要素になる「RDF」を選択してOk。
一覧となるリストの要素、コレクションとアイテムならコレクション側をサンプルデータ化するのがキモです。

これだけで、先ほどのクラスに適合したサンプルデータが取り敢えず自動生成されました。

データウインドウに挿入されたサンプルデータのツリーを開いて、実際に作成されたデータの型を確認してみます。
上手く出来ているでしょうか?
試しに、この中から、レコード一覧のコレクションデータである、itemをMainPage.xamlの真ん中にdrag&dropしてみます。

データの内容はランダムなテキストですが、取り敢えず何かが入ったGridViewのリストが出来ました。

何もないGridコントロール上にDrag&Dropするだけで、Blendが自動的に(Windowsストアアプリの場合)GridViewと簡単なItemTemplateまで定義してくれます。この辺りはBlend開発のセミナーなどでもよくデモされるところですね。
とりあえず、これで単純なサンプルデータができました。
変更されたMainPage.xamlをCtrl-Sで保存して、Blendを閉じ、Visual Studioへ戻ります。
Visual Studioへ戻ると変更を更新するかといってくるので「すべて再読み込み」

するとプロジェクトに先ほどのサンプルデータが/SampleData/RDFSampleData.xamlとして追加されているのが確認できると思います。
このファイルをダブルクリックして開きます。

と、このようなサンプルデータが作成されています。

<SampleModel:RDF xmlns:SampleModel="using:App5.SampleModel" xmlns:System="using:System">
	<SampleModel:RDF.channel>
		<SampleModel:channel about="Class nam curabitur">
			<SampleModel:channel.ItemsElementName>
				<SampleModel:ItemsChoiceType>description</SampleModel:ItemsChoiceType>
				<SampleModel:ItemsChoiceType>title</SampleModel:ItemsChoiceType>
				<SampleModel:ItemsChoiceType>info</SampleModel:ItemsChoiceType>
				<SampleModel:ItemsChoiceType>description</SampleModel:ItemsChoiceType>
				<SampleModel:ItemsChoiceType>title</SampleModel:ItemsChoiceType>
				<SampleModel:ItemsChoiceType>description</SampleModel:ItemsChoiceType>
				<SampleModel:ItemsChoiceType>info</SampleModel:ItemsChoiceType>
				<SampleModel:ItemsChoiceType>link1</SampleModel:ItemsChoiceType>
				<SampleModel:ItemsChoiceType>info</SampleModel:ItemsChoiceType>
				<SampleModel:ItemsChoiceType>info</SampleModel:ItemsChoiceType>
			</SampleModel:channel.ItemsElementName>
		</SampleModel:channel>
	</SampleModel:RDF.channel>
	<SampleModel:RDF.item>
		<SampleModel:item about="Nunc praesent accumsan vivamus" date="12/02/2013 13:26:52" description="Consequat dis quisque" encoded="Convallis est" link="Phasellus aliquam sed mauris integer" subject="Hac vestibulum maecenas duis curae" title="Aenean cras">
			<SampleModel:item.bookmarkcount>
				<System:UInt16/>
			</SampleModel:item.bookmarkcount>
		</SampleModel:item>
...

ストアアプリのPageや、WPFのWindowなどのルートオブジェクトが無く、直接"using:App5.SampleModel"などとXML名前空間を定義してオブジェクトが記述されているのがわかると思います。
ようするに、XAMLのサンプルデータとは、このような形式でマークアップされたデザイン用インスタンスの生成ということで、これさえ手で書ければBlendを使う必要も無いといえば無いのです(ちと面倒ですが)。
データが貼り付けられているGridViewそのものの定義は、

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" d:DataContext="{d:DesignData /SampleData/RDFSampleData.xaml}">
    <GridView ItemTemplate="{StaticResource itemTemplate}" ItemsSource="{Binding item}" IsSwipeEnabled="False" SelectionMode="Single"/>
</Grid>

GridViewが直接サンプルデータとBindしておらず、一段上のGridのデザインモードデータコンテキスト d:DataContextとして結合されているため、d:名前空間(xmlns:d="http://schemas.microsoft.com/expression/blend/2008")から参照されるサンプルデータはアプリの動作時には読み込まれず、アプリがWeb APIから取得したデータをDataContextに割り当てればよいわけです。

(xamlのリソースに等でサンプルデータを直書きする手法もあるのですが、こちらはアプリ実行時にもサンプルデータのインスタンスが生成されてしまいます。今回、分量が膨れてしまったため、この辺りは割愛)
さて、Blendの作成したサンプルデータはランダムなテキストが自動生成され、日付なども入っているので素敵といえばステキなのですが、せっかく元となったXMLRSSデータがあるわけですから、ここになんとかはめ込みたいというのが人情というもの(そうか?)。
元となるWeb APIデータ形式によるのですが、はてなRSSは比較的シンプルな要素型のXMLなので、ここでは要素の子要素として元の要素のデータを貼り付け、としてタグを修飾していくだけでほぼOkです。スキーマクラスが作成済みなので、入れ子になった子要素の定義もそのまま利用できます。
APIの応答がJSONの場合は、Bluehawk氏によるWebサービス XML-JSON相互変換ツールなどを利用すると良いのではないでしょうか。

XML-JSON相互変換ツール - Bluehawk's lab.
http://bluehawk.infinitybird.com/dev/xmljson.html

手作業ならVSが閉じタグを勝手に直してくれますが、いちいちやるのも手間なので、要領さえわかればあとはエディタの置き換え機能や正規表現などを活用してください。

これで、実データを差し込んだサンプルデータの作成そのものは完成です。

4. VSデザイナでコントロールのデザイン

で、ここまでくれば、あとはVSデザイナーのプレビューを確認しながらデータバインドされたItemTemplateを定義していくだけ。
というわけで、やや雑ですが、ちゃちゃっとデザインしてみたのが↓の状態。
サムネイル画像が取れていないのが残念ですが、今回はこれで精いっぱい。
これ、アプリ動いているわけじゃなく、VSデザイナーに差し込まれてるんですよ! ここまで切り張りのみでC#コーディングほぼ0。 もちろん、Blendで編集すればXAMLも見ずにデザインすることも可能。
実データが出ていると、ビルド→実行→確認→修正、というループが不要になるため、↑の状態から↓まで約5分程度。
本物から取得したサンプルデータでUIデザインできるって凄い重要だと思いませんか? というのが今回の目玉となります。

大まかな流れは以上です。
基本的には、クラス化→サンプルデータ作成→元データの差し込みという流れ。
使用するWeb APIXML/JSONの違いなどもあるかとは思いますが、ある程度汎用的に使えるパターンではないかと思います。
実際のアプリ実装では、取得した応答データのスキーマと、アプリ側のクラスが一致し無い場合や、VSの生成したスキーマ実装クラスの定義が微妙な名前付けになっている場合などがあるため、サンプルデータの生成前にある程度クラス定義を整えてから段取りを踏むことが多くなるのではないかと思います。最終的には、XAMLによりインスタンス生成されたサンプルデータと、それをスキーマ定義するクラスさえ一致していればいいわけで、今回の手順でどのようなクラスやデータが生成されているかを確認して、理解を深めていただければと思います。
結構細かい段取りや手間がかかるので、こんなのやってられない、と思われるかもしれませんが、デザイン時(design-time)にプレビューされるデータが実データに近ければ近いほど、デザインのクオリティや、後々のメンテナンス性として結果に影響してきます。コントロールやページの*.xamlファイルを開いただけで、あーこのコントロールか、などと認識できるメンテナンス性の高いプロジェクトを実現してみませんか?

それでは、みなさん今年の年末も良いXAMLを! Happy XAML Markup!

作成したサンプルプロジェクト:
(時間の都合上アプリ動作時のデータ取得部分は実装していません)

App5.zip 直

追記(2013/12/02):
なんと、VS2013からJSONの場合はd:DataContextにダイレクトに読み込ませることが可能になっていることがbiac山本さんの指摘により明らかに!
XMLでは使えないようですが、JSONの場合はかなりシンプルに読み込ませることもできそうです!

XAMLプログラミング WPFアプリケーションの概要と開発

XAMLプログラミング WPFアプリケーションの概要と開発

プログラミングWindows 第6版 上 (Microsoft Press)

プログラミングWindows 第6版 上 (Microsoft Press)