Quantcast
Channel: C#タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9517

C#でKey(変数)が定まっていないJsonをParseする

$
0
0

この記事で解説すること

Keyが定まっていないメンバーを含むJsonをParse(Desrialize)する方法

やりたいこと

Json形式はAPIのResponseなどとしてよく使われています。
Jsonの最小要素はKeyとValueのペアですが、このKey側があらかじめ定まっていない場合(静的でない)は、情報の取り出しに少し工夫がいります。
(C#での話です。Pythonだと"request"モジュールで自動的にParseしてくれたりします。)
(ちなみに、あらかじめKeyが定まっている場合はC#標準のDataContractJsonSerializerとかを使えば簡単にParseできます。)

// Jsonの最小要素// { "key" : value }の形で表される// この"key"の部分(ここでいう"name")が常に固定でない場合のParseをしたい。{"name":"Tanaka"}

Keyが定まっていないJsonの実例

題材がないと説明しにくいので、Keyが定まってないJsonの例を一つ書きます。
これは、SlackのWorkspaceに登録されている絵文字一覧を取得するAPIのResponseとして渡されるJsonです。

最初にある"ok":trueは、Requestが上手くできたかの値なのでここでは関係ありません。

問題は次の"emoji"に対応する項目です。
"bowtie", "squirrel".. というように、登録されている絵文字の名前がKeyとして並んでいます。しかし、登録されている絵文字はあらかじめ決まっていない(Slackではユーザーが新しく絵文字を追加さることができる)です。

つまり、"emoji"の中身の情報を取り出そうとすると、Keyが定まっていないJsonをParseする必要があります。

// ok, emojiがkeyとなるのは確定している。{"ok":true,"emoji":{// emojiの各項目は、Key=絵文字の名前: Value=絵文字画像のurl(またはalias)、という形式。// Key=絵文字の名前であり、それはあらかじめ定まっていないためParseに工夫が必要。  // また、emojiの項目数は固定ではない。"bowtie":"https://my.slack.com/emoji/bowtie/46ec6f2bb0.png","squirrel":"https://my.slack.com/emoji/squirrel/f35f40c0e0.png",// … (以下登録されている絵文字が続く)}}

この記事では、このJsonを例にとって、Parse方法を見ていきます。

環境

  • Windows10
  • VisualStudio2019 Community
  • C#8.0 (.NET Core3.1)
  • Newtonsoft.Json(Json.NET) v12.0.3

今回は、Newtonsoft.Json(Json.NET)を使ってParseを行います。Newtonsoft.JsonはNugetからインストールします。
(C#標準でもJsonを扱うためのクラス群が用意されているのですが、予めKey(文字列)が定まっていないJsonのParse方法はなさそうでした。やり方知っている方いれば教えてください。)

事前知識

本題に入る前に、Newtonsoft.Jsonの基本事項を確認しておきます。

Newtonsoft.Jsonを使ってJsonをParseする場合は、対象のJsonを、Jvalue, JObject, JArrayの3つの型に変換していく必要があります。Jvalue, JObject, JArrayの3つは、Parse対象のJsonの構成によって、適切に使い分ける必要があります。

(※Jsonの構成によっては、明示的にJValue, JObject, JArrayなどに変換しなくても、自動的に変換してくれるメソッドが用意されています。)

JValue, JObject, JArrayについて、以下に説明と簡単な例を示します。

  • JValue型 : プリミティブ型を表す。(文字列、数値など)
  • JObject型 : 単純なKeyとValueの羅列を表す。
  • JArray型 : 配列。いくつかのJObjectをひとまとめにしたものを表す。
  • JToken型:JValue, JObject, JArrayのベースクラス。
    (ちなみに、今回のParseで使うのはJObjectだけです。)
// {}のまとまりがJObject (Taroや24はJValue){"name":"Taro","age":24}// []のまとまりがJArray[{"name":"Taro"},{"age":24},...]

本題

まずParseしたいJsonの構成を見て、使う型を選びます。

下記のJsonなら以下のようにParseできそうです。(今回はJObject型のみ使用)

  1. 全体をJObject型で受け取る。(全体 = 一番外側の"ok"や"emoji"を含む{}のこと)
  2. 1の中から"emoji"のValue("emoji" : 以降の{}の中身全体)をJObject型で受け取る
  3. 2の中から各項目をKeyValuePairとして取り出す
{"ok":true,"emoji":{// emojiの各項目は、Key=絵文字の名前: Value=絵文字画像のurl(またはalias)、という形式。// Key=絵文字の名前が不定のため特別にParseする必要がある。  // また、emojiの項目数は固定ではない。"bowtie":"https://my.slack.com/emoji/bowtie/46ec6f2bb0.png","squirrel":"https://my.slack.com/emoji/squirrel/f35f40c0e0.png",}}

実際に、1~3までの流れをコードで書くとこうです。

stringjsonString=(↑に示したJsonが代入されているとする)// 1. 全体をJObject型で受け取る。// jsonStringはResponseなどで受け取った、json構造のstring型変数とする。// string --> JObjectの変換はJObject.Parse()で行う。JObjectjsonObject=JObject.Parse(jsonString);// 2. 1の中から"emoji"のValueをJObject型で受け取る。// ["(KeyName)"]で特定のKeyのValueを、JObjectから取り出せる。// ここでは取り出すValueもJObject型のため、JObject型の変数に代入してやる。明示的なCastが必要。JObjectemojis=(JObject)jsonObject["emoji"];// 3. 2の中から各項目をKeyValuePairとして取り出す// JObject型はIEnumerableを継承しており、// GetEnumerator()でKeyValuePair<string, JToken?>を返す。// 上の例では、KeyValuePairのKeyが絵文字の名前、Valueが絵文字のURIとなる。foreach(varemojiinemojis){varname=emoji.Key;// KeyValuePairのValueはJToken?型のため、stringに変換する。varuri=newUri(emoji.Value.ToString());Console.WriteLine($"EmojiName : {name}, EmojiUri : {uri}");}// ============================// Output : // EmojiName : bowtie, EmojiUri : https://my.slack.com/emoji/bowtie/46ec6f2bb0.png// EmojiName : squirrel, EmojiUri : https://my.slack.com/emoji/squirrel/f35f40c0e0.png// ...

コード中にここまでで解説していないものがいくつかあるので、補足していきます。

  • JObject.Parse()
JObjectjsonObject=JObject.Parse(jsonString);

引数に与えられたstring型をJObject型に変換するメソッドです。
コード中では、まずこのメソッドを使って、Responseなどで受け取ったstring型をJObjectに変換しています。

なお、引数に渡すstring型はJson形式になっている必要があります。(Json形式になっていない場合は、Newtonsoft.Json.JsonReaderExceptionがthrowされます。)

  • Keyを指定したValueの取り出し
JObjectemojis=(JObject)jsonObject["emoji"];

JObject型から特定のKeyに対応するValueを取り出したい場合は、Dictionary型などと同様に、["(Key)"]の形で指定してやると取り出すことができます。
この際、取り出したValueを適切な型にCastしてやる必要があります。(正確にいうと、この時点ではCastしなくても大丈夫です。Castしない場合はJToken型となります。)

例ではJObject型にCastしていますが、JArray型にCast使用とすると例外(System.InvalidCastException)がthrowされます。

  • JObjectからの個別要素の取り出し
// emojisがJObject// emojisの実態は、//      "bowtie": "https://my.slack.com/emoji/bowtie/46ec6f2bb0.png"//      "squirrel": "https://my.slack.com/emoji/squirrel/f35f40c0e0.png", ...// というKeyとValueの羅列foreach(varemojiinemojis){....}

ここで、変数emojisはJObject型であり、その実態はKeyとValueの羅列です。

JObject型はIEnumerableを継承しており、foreachで扱えます。
このとき、各要素(GetEnumerator()の戻り値)は、KeyValuePairとなります。(↑の例だと、Keyのstringが絵文字の名前、Valueが絵文字のuriとなります)

なお、KeyValuePairのValueは、JToken型となるので利用するにはCastが必要となります。

まとめ

この記事では、Keyの名前が静的でないJsonのParse(Deserialize)方法を書きました。

簡潔にまとめると以下です。

  • ParseしたいJsonの構成を見て、使う型(JObject, JArray, JValueなど)を選ぶ。
  • string型をJObject.Parseで変換し、Newtonsoft.jsonで扱えるようにする。
  • Json全体から、必要な部分を、選んだ型に変換しつつ取り出していく。
  • Keyの名前が静的でない部分は、JObjectからKeyValuePairを受け取ることにより、取り出す。

各種リンク


Viewing all articles
Browse latest Browse all 9517

Trending Articles