C#(Core 2.1)でJSONをデシリアイズする（１）～ DataContractJsonSerializer ～

.Net framework の標準機能である DataContractJsonSerializer で JSON のデシリアイズします。有名なところだと Json.NET などがありますが、諸事情でそれらのライブラリが使えないような場合、この方法が選択します。
サンプルでは C# から HTTP リクエストを送って、サーバ側の CakePHP が JSON を返し、それをデシリアイズしています。
[DataContractJsonSerializer Class][serializer]

# 環境
- クライアント
  - Windows 10
  - Visual Studio 2019
  - .Net Core 2.1
- サーバー
  - Ubuntu 18.04.2 LTS (WSL)
  - Apache 2.4
  - CakePHP 3.8
  - PHP 7.2.19
  - MariaDB 10.3.15

サーバ側は何でもよかったのですが、今仕事で使っている CakePHP にしました。Ubuntu は WSL 上で動いています。

# レスポンスJSON

```Javascript
[
    {
        "id":1,
        "name":"田中望",
        "nickname":"バカ",
        "birthday":"8月10日",
        "constellation":"しし",
        "blood_type":"O",
        "height":158,
        "weight":41
    },
    {
        "id":2,
        "name":"菊池茜",
        "nickname":"ヲタ",
        "birthday":"3月3日",
        "constellation":"うお",
        "blood_type":"O",
        "height":160,
        "weight":50
    },
    // ...
]
```

# クライアントサイド

###### リクエスト送信、レスポンス受信処理

１．GetResponse メソッドでリクエスト送信とレスポンス受信を行います。
２．取得したレスポンスのストリームを MemoryStream に代入します。
３．DataContractJsonSerializer クラスの ReadObject でデシリアイズします。値のセットは次項の「JSON 格納用の POCO」の記述に従います。

```C#:Program.cs
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Runtime.Serialization.Json; // DataContractJsonSerializer が入っています。
using System.Text;

namespace WebApiReciever
{
    class Program
        static void Main(string[] args)
        {
            var url = @"http://localhost:8080/~urushibata/my_cake/Person/rest";
            var req = WebRequest.Create(url);
            req.Method = "GET";
            var res = req.GetResponse(); // １．リクエスト送信

            using (var reader = new StreamReader(res.GetResponseStream()))
            // ２．UTF8 に変換し MemoryStream に格納します。
            using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(reader.ReadToEnd())))
            {
                // デシリアイズする型を指定します。
                var serializer = new DataContractJsonSerializer(typeof(IList<Person>));
                // デシリアイズします。
                var persons = (IList<Person>)serializer.ReadObject(ms);

                Debug.WriteLine($"名前\t通称\t誕生日\t星座\t血液型\t身長\t体重");
                foreach(var p in persons)
                {
                    Debug.WriteLine($"{p.Name}\t{p.Nickname}\t{p.Birthday}\t{p.Constellatione}\t"
                                         + $"{p.BloodType}\t{p.Height}\t{p.Weight}");
                }
            }
        }
    }
}
```
###### JSON 格納用の POCO

クラスに DataContractAttribute を付与することでシリアライズ化可能とします。
フィールドメンバーに DataMemberAttribute を付与するとシリアライズの対象となります。対象外としたい場合は、IgnoreDataMemberAttribute を付与します。
JSON のプロパティ名と異なるプロパティにセットしたい場合、DataMemberAttribute の Name プロパティで JSON のプロパティ名をマッピングできます。(C# の命名規約(パスカルケース)と CakePHP の命名規約(スネークケース)は相性悪いですな…)

```C#:Person.cs
using System.Runtime.Serialization;

namespace WebApiReciever
{
    [DataContract]
    public class Person
    {
        [DataMember(Name = "id")]
        public string Id { get; set; }

        [DataMember(Name = "name")]
        public string Name { get; set; }

        [DataMember(Name = "nickname")]
        public string Nickname { get; set; }

        [DataMember(Name = "birthday")]
        public string Birthday { get; set; }

        [DataMember(Name = "constellatione")]
        public string Constellatione { get; set; }

        [DataMember(Name = "blood_type")]
        public string BloodType { get; set; }

        [DataMember(Name = "height")]
        public int Height { get; set; }

        [DataMember(Name = "weight")]
        public int Weight { get; set; }
    }
}
```

# サーバーサイド

処理内容は、person テーブルを全件取得して、JSON 文字列に変換して返します。
今回の主題とは離れるので詳細は割愛します。

```PHP:PersonController.php
<?php
namespace App\Controller;

use App\Controller\AppController;

final class PersonController extends AppController
{
    public function rest()
    {
        $this->autoRender = false;
        $this->response->type('application/json');
    
        $person = $this->Person->find()->all();
        // 第2引数に JSON_UNESCAPED_UNICODE を入れないと、マルチバイト文字は \u30ef というようにコードポイントになります。
        $this->response->body(json_encode($person, JSON_UNESCAPED_UNICODE));
    }
}
```

# 結果

「女子高生の無駄遣い」のキャラクターデータを取得できました。

```
名前	通称	誕生日	星座	血液型	身長	体重
田中望	バカ	8月10日			158	41
菊池茜	ヲタ	3月3日			160	50
鷺宮しおり	ロボ	10月24日			155	41
百井咲久	ロリ	7月21日			142	35
山本美波	ヤマイ	4月17日			159	39
一奏	マジメ	5月9日			170	55
染谷リリィ	リリィ	9月12日			164	44
久条翡翠	マジョ	6月6日			157	42
佐渡正敬	ワセダ	10月21日			178	65
```

[C# で JSON をシリアライズ、デシリアライズする][sansho1]

[serializer]:https://docs.microsoft.com/ja-jp/dotnet/api/system.runtime.serialization.json.datacontractjsonserializer?view=netframework-4.8
[sansho1]:https://takachan.hatenablog.com/entry/2017/01/18/120000