Windows::Data::Json
JSONパーサをもう一つ、Windows Storeアプリの実行環境:Windows Runtimeが提供する名前空間Windows::Data::Json配下のクラス群です。Storeアプリはマシン上のストレージに対するアクセスにはかなり厳しい制限を課しており、そのかわりネットワークにあるストレージ/サービスを簡単に使えるよう工夫されています。Windows RuntimeがJSONパーサを用意しているのもその一環なのでしょう。
名前空間Windows::Data::JsonにはvalueのインターフェースIJsonValueと、それを実装したJsonValue,JsonArray,JsonObjectが用意されています。JSON textをパースするにはstaticメソッド:JsonValue::Parse(String^)にJSON textを与えるとJsonValue^が返ってきます。JsonValueから値を取り出すにはGetXXX():XXXはBoolean,Number,String,Array,Objectのいずれか。JsonValueが内包する値の型はValueTypeプロパティに型IDが入っています。またJsonArrayのn番目のvalueはJsonArray::GetXXXAt(n)、JSonObject内のkeyに対応するvalueはGetNamedXXX(key)で得られます。
それではサンプルを。Webから拾ったJSON textをパースしてみましょうか。
僕が利用しているサンプルコード投稿サイト:Qiitaは、投稿一つ一つに言語タグが付与されます。例えば「C++」とか「Java」とか。利用者は興味のあるタグの付いた投稿だけをブラウズってスンポーです。
ありがたいことにQiitaは開発者APIを公開してくれてまして、これを使ってQiita専用アプリが作れます。その開発者APIが返してくるのがJSON textなんですわ。例えば登録されているタグ一覧を取得するには https://qiita.com/api/v1/tags/ をHTTP GETします。すると、タグのリストがJSON textで返ってきます。
今回Storeアプリのお話は本筋じゃないので、C++/CXでコンソールアプリに仕立てます。Windows Runtimeを使ったC++/CXコンソールアプリは参照設定などややこしい準備が必要なのですが、「CxxWinRTTemplate for Windows 8.1」という拡張機能をVisual Studioにインストールすると、コンソールアプリのひな型があっさり作れちゃいます。インストールと使い方をQiitaに投稿しておいたので併せてご一読ください。
Windows RuntimeのおかげでHTTP GETを投げて応答を受け取るコードはほんの数行で書けてしまいますから、あとはJSONパーサに食わすだけ。ただしHTTP GETを投げてその応答を受け取るのは、ネットワーク越しであるがゆえそれ相応の時間がかかります。Windows Runtimeでは長時間ベタ待ちになるのは御法度で、非同期処理を扱わにゃなりません。「Visul C++ Compiler November 2013 CTP」で追加された resumable/await で楽させていただきました。
#include <ppltasks.h>
#include <pplawait.h>
#include <functional>
#include <cassert>
using namespace concurrency;
using namespace Windows::Foundation;
using namespace Windows::Data::Json;
task<void> getTags(std::function<void(Platform::String^)> fun) __resumable {
auto client = ref new Windows::Web::Http::HttpClient();
auto jval = JsonValue::Parse(
__await client->GetStringAsync(
ref new Uri(L"https://qiita.com/api/v1/tags")));
assert( jval->ValueType == JsonValueType::Array );
auto jary = jval->GetArray();
for ( unsigned int i = 0; i < jary->Size; ++i ) {
fun(jary->GetObjectAt(i)->GetNamedString(L"name"));
}
}
int main(Platform::Array<Platform::String^>^) {
auto task = getTags([](Platform::String^ name) {
Platform::Details::Console::WriteLine(name);
});
task.wait();
return 0;
}
/* 実行結果:
Qiita
Java
Ruby
Python
Perl
PHP
Emacs
Vim
ShellScript
C
C++
Objective-C
HTML
...
*/
せっかくだからStoreアプリでもやってみました。コンソールと大きな差はありませんね。
//
// MainPage.xaml.cpp
// MainPage クラスの実装。
//
#include "pch.h"
#include <pplawait.h>
#include "MainPage.xaml.h"
using namespace App1;
using namespace Platform;
using namespace Windows::Foundation;
using namespace Windows::UI::Xaml;
using namespace Windows::Web::Http;
using namespace Windows::Data::Json;
/*
<ListView x:Name="TagList" >
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" FontSize="32"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
*/
MainPage::MainPage() {
InitializeComponent();
}
void App1::MainPage::Button_Click(Object^ sender, RoutedEventArgs^ e) __resumable {
/* GET https://qiita.com/api/v1/tags
* により、Qittaからタグ列を手に入れる。
*/
HttpClient^ client = ref new HttpClient();
String^ response = __await client->GetStringAsync(ref new Uri(L"https://qiita.com/api/v1/tags"));
/* Qiita-APIから手に入れた JSON文字列を
Windows::Data::Json::JsonValue::Parse でパースし、JsonValueを手に入れてJsonArrayを取り出す。
*/
JsonValue^ val = JsonValue::Parse(response);
JsonArray^ ary = val->GetArray();
/* ListView::ItemsourceにVector<String^>^ を与える。
*/
auto tags = ref new Platform::Collections::Vector<String^>();
for ( unsigned int i = 0; i < ary->Size; ++i ) {
tags->Append(ary->GetObjectAt(i)->GetNamedString(L"name"));
}
TagList->ItemsSource = tags;
}
...なんだかさっくりできちゃったけど、いかがでしょう。JSONはXMLよりずっとシンプル。スキーマ定義なくても受け取る側があらかじめその構造を知ってさえいれば問題ないわけで、通信を伴うアプリケーション実装の手駒の一つとして、知っててソンはありませんです。

