こちらで公開しております。
- GitHub [GistsApi] (https://github.com/pierre3/GistsApi)
Gist API が返してくるJSONの変換にはDynamicJsonを使用させていただきました。
GistのJSONをDynamicJsonでらくらく一発変換!と行きたかったのですが、一発でとはいかずちょっとばかり悩んだ所があったのでメモ。
GistsAPIが返すJSONとそれに対応するC#のObjectがこちらです。
- GistObjects.json
- GistObjects.cs
通常であれば以下のようにして簡単・楽ちんなのですが
//IDで定したGistをとって来てJSONの文字列で返す
var json = await gistClient.GetASingleGist("gistId");
//キャストするだけ!
var gist = (GistObject)DynamicJson.Parse(json);
しかし、これだとGistObject.Files
に相当する部分が変換できません。
以下のように"files"内の1ファイルを表すJSON要素の名前(key)がファイル名になっていて不定であることがその原因です。
"files": {
"ring.erl": {
"size": 932,
"filename": "ring.erl",
"raw_url": "https://gist.github.com/raw/365370/8c4d2d43d178df44f4c03a7f2ac0ff512853564e/ring.erl"
}
},
DynamicJson では変換先Objectのプロパティ名をJSONの名前、プロパティの値をJSONの値として変換を行っているのですが、事前に名前がわかっていないと変換先であるObjectを記述することができません。
class Files
{
//これが宣言できない。そもそも名前に 「.」 が入っているし
public File ring.erl{set;get;}
}
// それで、こういう実装にしてみても
class Files : Collection<File>
{
}
gist.Files = new []{
new File(){
size = json.Files.(ファイル名).size,
filename = json.Files.(ファイル名).filename
raw_url = json.Files.(ファイル名).raw_url
},
...
};
//結局(ファイル名)のところが記述できない。。。
で、どうするかというところで悩んでしまいました。 が、悩んだ末に、DynamicJsonの解説を再確認してみると、ある見落としに気づきました。
var r4 = json["nest"]["foobar"]; // can access indexer
ということで、インデクサです。
DynamicJsonには、GetDynamicMemberNames()という ある要素の子要素の名前を列挙してくれるメソッドがありますので、これで名前を取得してインデクサでアクセスする。という寸法です。
//files の子要素の名前を列挙して、それをキーにインデクサでアクセスする
DynamicJson files = json.files;
foreach (var name in files.GetDynamicMemberNames())
{
gist.Files.Add(new File(){
filename = files[name].filename,
size = files[name].size,
raw_url = files[name].raw_url
});
}
せっかくなので、上記処理を拡張メソッドに切り出しましょう。 最終的な変換処理は、以下のような実装になりました。
public static class DynamicJsonExtensions
{
public static IEnumerable<T> DeserializeMembers<T>(this DynamicJson dynamicJson, Func<dynamic, T> resultSelector)
{
foreach (var name in dynamicJson.GetDynamicMemberNames())
{
yield return resultSelector(((dynamic)dynamicJson)[name]);
}
}
}
protected static GistObject DynamicToGistObject(dynamic json)
{
//files以外の要素はキャストでOK
var gist = (GistObject)json;
//DynamicJsonの子要素を、その名前を元に取得して列挙しデリゲートに渡します。
//デリゲートには目的のオブジェクトに変換する処理を記載します。
var files = ((DynamicJson)json.files).DeserializeMembers(member =>
new File()
{
filename = member.filename,
raw_url = member.raw_url,
size = member.size
});
//本体のGistObjectに設定
gist.files = new Files(files.ToArray());
return gist;
}