JsonValueのメモalue

導入方法

C#用のJSONパーサーに何が良いのか分からないがMS謹製のSystem.Jsonを使って見る。他にDynamicJsonも良さそうだが開発者自身がSystem.Json禅譲と言っているのでパスした。

System.Jsonは元々SliverLight用のライブラリだったようだが.Net用にもNuGetでインストールして利用できる。下のページにあるようにVisualStudioのNuGetコンソールから叩けば良い。正式版が出ていないようなので-Versionオプションまで含める必要がある

NuGet Gallery | System.Json 4.5.0

他にも別名のJsonValueというものもあるようだがこちらも古いまま宙ぶらりんになっている模様。もし最新の開発状況などご存知なら教えて下さい。

JsonArrayのLINQが効かない

これが本題。JsonArrayはJSONの配列部分を取得した時に、JsonValueの実装サブクラスのインスタンスとして得られる。これはIEnumerableを継承しており、通常通りLINQのSelectやらWhereやらで操作できるはずなのだが、インテリセンスが効かないし、コーディングしてもコンパイルエラーが出る。(list1の箇所)

using System.Json;

namespace ConsoleApplication1 {
    class Program {
        static void Main(string[] args) {

            var json = JsonObject.Parse(@"{ ""Items"" : [ {""Name"":""いちじく""} , {""Name"":""にんじん""} , {""Name"":""さんしょ""} ]}");
            var array = (JsonArray)json["Items"];

            var list1 = array.Select(x => x["Name"].ReadAs<string>());     //コンパイルエラー
            var list2 = ((IEnumerable<JsonValue>)array).Select(x => x["Name"].ReadAs<string>());
            var list3 = array.Select<JsonValue,string>(x => x["Name"].ReadAs<string>());
            var list4 = array.AsJsonArray().Select(x => x["Name"].ReadAs<string>());
            var list5 = json["Items"].AsJsonArray().Select(x => x.ReadAs<string>());

            list4.ToList().ForEach(Console.WriteLine);
        }
    }

    static class JsonValueExtention {
        public static IEnumerable<JsonValue> AsJsonArray(this JsonValue json) { return (IEnumerable<JsonValue>)json; }
    }
}

どうも原因は、JsonArrayはIEnumerableだけでなく、親クラス経由でIEnumerable< KeyValuePair >も継承しているので、どちらのIEnumerableのLINQ操作なのか曖昧なことが原因のようだ。実際にlist2のコードのようにIEnuerableに明示的にキャストすると意図通り動いてくれる。ただ少々面倒くさいし、可読性が落ちる。list3のようにSelect拡張メソッドに明示的に型推論のヒントを与えることでも解決できるが、こちらも少々煩雑。
どうしてもワンライナーのメソッドチェーンで書きたければ、list4のようにキャスト用の拡張メソッドを用意してやるのがスマートかもしれない。list5のようにより短く書くこともできる。
なお、System.Json名前空間のJsonValueLinqExtentionsクラスにToJsonArray拡張メソッドがあるが、これはどうも目的が全く違いもののようなので自作が必要だ。