Neutral Scent

App developments & Gadgets

DataContractJsonSerializerで不定形のJSONデータを読み込む

Windows Phone 7アプリでJSONを読み込もうとすると、MangoからはDataContractJsonSerializerを使ってね、というのが標準でのアプローチのようです。
ただ、DataContractJsonSerializerは名前の通りDataContract系で、静的な型付けが必須。Deserializeした読込先のクラスをきっちり定義してやる必要があります。
が、相手は柔軟性の権化ともいうべきJavaScript、そう簡単には問屋が下ろしません。各種のWeb APIを呼んでみると想定外の非定型データが飛んでくることもあります。
今回引っかかったのははてなスターAPI(いちおう公式には公開されていないかもしれませんが)。http://s.hatena.ne.jp/いろいろ投げるとスター情報を返してくれますが...。

こんな例外が出るわけです。
ひとしきり試行錯誤してわかったのは、以下のようなデータが返ってきてコケている、ということ。



{
"entries":[
{
"stars":[
{"quote":"","name":"ujuki"},
{"quote":"","name":"rs6000moe"}
],
"can_comment":0,
"uri":"http://b.hatena.ne.jp/mena/20111020#bookmark-63866253"
},
{
"stars":[
{"quote":"","name":"white_rose"},
20,
{"quote":"","name":"k_gobo"}
],
"can_comment":0,
"uri":"http://b.hatena.ne.jp/tetzl/20111020#bookmark-63866253"
},
{
"stars":[
{"quote":"","name":"flagburner"}
],
"can_comment":0,
"uri":"http://b.hatena.ne.jp/wackunnpapa/20111020#bookmark-63866253"
},
"can_comment":"0",
"rks":null
}
配列の中にintがっ! まー、静的に型付けされてれば、そりゃコケますよね。
ちなみに生成エラーとかではなく、そういう仕様らしいです。☆20☆みたいに出る数字ですね...。
読み込もうとしてるクラスはこちら(一部のみ抜粋)、

[DataContract]
public class BookmarkStarEntry : INotifyPropertyChanged
{
private HatenaStar[] starsVal;
[DataMember]
public HatenaStar[] stars
{
get { return starsVal; }
set
{
if (value != starsVal)
{
starsVal = value;
NotifyPropertyChanged("stars");
}
}
}
で、他のライブラリを使うとかいろいろ回避策を考えたのですが、ぼんやり例外を眺めているときに思いつきました。
InvalidCastExceptionなんだから、キャストできる形にしてあげれば、objectにしちゃえばいいじゃない。
で、これ、

[DataContract]
public class BookmarkStarEntry : INotifyPropertyChanged
{
private object[] starsVal;
[DataMember]
public object[] stars
{
get { return starsVal; }
set
{
if (value != starsVal)
{
starsVal = value;
NotifyPropertyChanged("stars");

CountStars(value);
}
}
}

試してみたところ、とりあえずもくろみ通り、例外も出ずに読み込めるようになったので、あとは読み込んでからstarsをisやasで適時読み取ってやればOkというわけです。

private void CountStars(object[] stars)
{
int count = 0;
foreach (var star in stars)
{
if (star is int)
count += (int)star - 1;
}
starCount = count + stars.Length;
}
というわけで、一見頭の固そうなDataContractJsonSerializerも何が起きているのか考えてあげれば、意外と柔軟に使えそうですよ、という話でした。