Skip to content

Instantly share code, notes, and snippets.

@pierre3
Last active February 26, 2018 05:14
Show Gist options
  • Save pierre3/5381825 to your computer and use it in GitHub Desktop.
Save pierre3/5381825 to your computer and use it in GitHub Desktop.
[DynamicJson] JSON 要素の名前(key)部分が不定の場合の変換方法

[DynamicJson] JSON の名前(key)が不定の場合の変換方法について

DynamicJson とSystem.Net.Http.HttpClient でGists APIを作ってみました

こちらで公開しております。

Gist API が返してくるJSONの変換にはDynamicJsonを使用させていただきました。

GistのJSONをDynamicJsonでらくらく一発変換!と行きたかったのですが、一発でとはいかずちょっとばかり悩んだ所があったのでメモ。

Gists Object

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;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment