今更ですが、C#でJsonを扱う

# はじめに

あるようで、なかったので。`Newtonsoft.Json`の使い方です。`DataContractJsonSerializer`ではありません。

# 準備

- 「参照」を右クリック、「NuGet パッケージの管理...」

![2020-03-18_141313.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208281/7e8a11d0-a64d-8e7a-b5d2-cd2de479a4f4.png)

- 「参照」タブから、「Newtonsoft.Json」を選択して、インストールする

![2020-03-18_141424.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208281/60bee40f-946a-c186-fb49-13c74abf3b0c.png)


# 基本

`JsonConvert.SerializeObject()` でシリアル化（オブジェクト → 文字列）、`JsonConvert.DeserializeObject<T>()` でデシリアル化（文字列 → オブジェクト）。

※サンプルなので、`Formatting.Indented` を指定して、JSONを見やすく改行して出力するようにしていますが、実際には無くても良いです。

```cs
class Account
{
    public enum AccountRole { Role1, Role2, Role3 }

    public int ID { get; set; }
    public string Name { get; set; }
    public bool IsActive { get; set; }
    public DateTimeOffset CreatedDate { get; set; }
    public AccountRole Role { get; set; }
    public IList<string> Telephones { get; set; } = new List<string>();
}

class Program
{
    static void Main(string[] args)
    {
        // シリアライズ
        var account = new Account()
        {
            ID = 1,
            Name = @"hoge@example.com",
            IsActive = true,
            CreatedDate = DateTimeOffset.Now,
            Role = Account.AccountRole.Role1,
            Telephones = new List<string>()
            {
                "010-1111-2222",
                "020-2222-3333",
            },
        };

        var json = JsonConvert.SerializeObject(account, Formatting.Indented);
        Console.WriteLine(json);
        Console.WriteLine("\n---");

        // デシリアライズ
        var obj = JsonConvert.DeserializeObject<Account>(json);
        Console.WriteLine(string.Join("\n", typeof(Account).GetProperties().Select(info => $"{info.Name}: {info.GetValue(obj)}")));

        Console.ReadLine();
    }
}
```

出力

```console
{
  "ID": 1,
  "Name": "hoge@example.com",
  "IsActive": true,
  "CreatedDate": "2020-03-18T14:29:30.1143602+09:00",
  "Role": 0,
  "Telephones": [
    "010-1111-2222",
    "020-2222-3333"
  ]
}

---
ID: 1
Name: hoge@example.com
IsActive: True
CreatedDate: 2020/03/18 14:28:21 +09:00
Role: Role1
Telephones: System.Collections.Generic.List`1[System.String]
```

## enum のシリアライズ

C#では、enumは内部では数値なので、デフォルトでは数値で出力されてしまう。`JsonConverterAttribute`を使って、`StringEnumConverter`を指定すると、文字列にできる。

```cs
class Account
{
    public enum AccountRole { Role1, Role2, Role3 }

    public int ID { get; set; }
    public string Name { get; set; }
    public bool IsActive { get; set; }
    public DateTimeOffset CreatedDate { get; set; }
    [JsonConverter(typeof(StringEnumConverter))]
    public AccountRole Role { get; set; }
    public IList<string> Telephones { get; set; } = new List<string>();
}

class Program
{
    static void Main(string[] args)
    {
        // シリアライズ
        var account = new Account()
        {
            ID = 1,
            Name = @"hoge@example.com",
            IsActive = true,
            CreatedDate = DateTimeOffset.Now,
            Role = Account.AccountRole.Role1,
            Telephones = new List<string>()
            {
                "010-1111-2222",
                "020-2222-3333",
            },
        };

        var json = JsonConvert.SerializeObject(account, Formatting.Indented);
        Console.WriteLine(json);

        Console.ReadLine();
    }
}
```

出力

```console
{
  "ID": 1,
  "Name": "hoge@example.com",
  "IsActive": true,
  "CreatedDate": "2020-03-18T15:01:36.37789+09:00",
  "Role": "Role1",
  "Telephones": [
    "010-1111-2222",
    "020-2222-3333"
  ]
}
```

## enumのデシリアライズ

なぜか、`JsonConverterAttribute`を付けていなくても、解釈できる。（しかし、シリアライズのことを考えると、`JsonConverterAttribute`を付けておいたほうが良いと思う）

```cs
class Account
{
    public enum AccountRole { Role1, Role2, Role3 }

    public int ID { get; set; }
    public string Name { get; set; }
    public bool IsActive { get; set; }
    public DateTimeOffset CreatedDate { get; set; }
    // [JsonConverter]付けない
    public AccountRole Role { get; set; }
    public IList<string> Telephones { get; set; } = new List<string>();
}

class Program
{
    static void Main(string[] args)
    {
        // デシリアライズ
        var json = $@"{{
""ID"": 999, 
""Name"": ""foo"", 
""IsActive"": true, 
""CreatedDate"": ""2020-01-02T03:04:05+09:00"", 
""Role"": ""Role2""
}}";

        var obj = JsonConvert.DeserializeObject<Account>(json);
        Console.WriteLine(string.Join("\n", typeof(Account).GetProperties().Select(info => $"{info.Name}: {info.GetValue(obj)}")));

        Console.ReadLine();
    }
}
```

出力

```console
ID: 999
Name: foo
IsActive: True
CreatedDate: 2020/01/02 3:04:05 +09:00
Role: Role2
Telephones: System.Collections.Generic.List`1[System.String]
```

Jsonに enum に対応する属性がないと、enum は値型なので、`null` ではなく `0` に対応する値になってしまう。

```cs
class Program
{
    static void Main(string[] args)
    {
        // デシリアライズ
        var json = $@"{
""ID"": 999, 
""Name": ""foo"", 
""IsActive"": true, 
""CreatedDate"": ""2020-01-02T03:04:05+09:00""
}}";

        var obj = JsonConvert.DeserializeObject<Account>(json);
        Console.WriteLine(string.Join("\n", typeof(Account).GetProperties().Select(info => $"{info.Name}: {info.GetValue(obj)}")));

        Console.ReadLine();
    }
}
```

出力（`Role` が `0` に対応する `Role1` になっている）

```console
ID: 999
Name: foo
IsActive: True
CreatedDate: 2020/01/02 3:04:05 +09:00
Role: Role1
Telephones: System.Collections.Generic.List`1[System.String]
```

Json の enum に対応する値が `null` だと、例外を throw してしまう。

```cs
class Program
{
    static void Main(string[] args)
    {
        // デシリアライズ
        var json = $@"{
""ID"": 999, 
""Name"": ""foo"", 
""IsActive"": true, 
""CreatedDate"": ""2020-01-02T03:04:05+09:00"", 
""Role"": null
}}";

        var obj = JsonConvert.DeserializeObject<Account>(json);
        Console.WriteLine(string.Join("\n", typeof(Account).GetProperties().Select(info => $"{info.Name}: {info.GetValue(obj)}")));

        Console.ReadLine();
    }
}
```

![2020-03-19_155612.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208281/0ac377cf-7dd7-b5f6-ac6c-642091659203.png)

## DateTime のシリアライズ

### 一括で指定

`JsonSerializerSettings`の`DateFormatHandling`プロパティでフォーマットを指定する。しかし、`MicrosoftDateFormat`と`IsoDateFormat`(`+0900`というオフセットが付く、[ISO 8601](https://ja.wikipedia.org/wiki/ISO_8601)の書式)しか無い。

```cs
class Account
{
    public enum AccountRole { Role1, Role2, Role3 }

    public int ID { get; set; }
    public string Name { get; set; }
    public bool IsActive { get; set; }
    public DateTimeOffset CreatedDate { get; set; }
    [JsonConverter(typeof(StringEnumConverter))]
    public AccountRole Role { get; set; }
    public IList<string> Telephones { get; set; } = new List<string>();
}

class Program
{
    static void Main(string[] args)
    {
        // シリアライズ
        var account = new Account()
        {
            ID = 1,
            Name = @"hoge@example.com",
            IsActive = true,
            CreatedDate = DateTimeOffset.Now,
            Role = Account.AccountRole.Role1,
            Telephones = new List<string>()
            {
                "010-1111-2222",
                "020-2222-3333",
            },
        };

        JsonSerializerSettings settings = new JsonSerializerSettings()
        {
            DateFormatHandling = DateFormatHandling.MicrosoftDateFormat,
            Formatting = Formatting.Indented,
        };
        var json = JsonConvert.SerializeObject(account, settings);
        Console.WriteLine(json);

        Console.ReadLine();
    }
}
```

出力

```console
{
  "ID": 1,
  "Name": "hoge@example.com",
  "IsActive": true,
  "CreatedDate": "\/Date(1584586457933+0900)\/",
  "Role": "Role1",
  "Telephones": [
    "010-1111-2222",
    "020-2222-3333"
  ]
}
```

### 個別に指定

`JsonConverterAttribute`で指定する。

```cs
class Account
{
    public enum AccountRole { Role1, Role2, Role3 }

    public int ID { get; set; }
    public string Name { get; set; }
    public bool IsActive { get; set; }
    [JsonConverter(typeof(IsoDateTimeConverter))]
    public DateTimeOffset CreatedDate { get; set; }
    [JsonConverter(typeof(StringEnumConverter))]
    public AccountRole Role { get; set; }
    public IList<string> Telephones { get; set; } = new List<string>();
}

class Program
{
    static void Main(string[] args)
    {
        // シリアライズ
        var account = new Account()
        {
            ID = 1,
            Name = @"hoge@example.com",
            IsActive = true,
            CreatedDate = DateTimeOffset.Now,
            Role = Account.AccountRole.Role1,
            Telephones = new List<string>()
            {
                "010-1111-2222",
                "020-2222-3333",
            },
        };

        var json = JsonConvert.SerializeObject(account, Formatting.Indented);
        Console.WriteLine(json);

        Console.ReadLine();
    }
}
```

出力

```console
{
  "ID": 1,
  "Name": "hoge@example.com",
  "IsActive": true,
  "CreatedDate": "2020-03-19T14:46:17.3561825+09:00",
  "Role": "Role1",
  "Telephones": [
    "010-1111-2222",
    "020-2222-3333"
  ]
}
```

### フォーマットを自分で指定

フォーマット指定ができるConverterが無いので、自分でそういうConverterを作るしかない。`IsoDateTimeConverter`の`DateTimeFormat`プロパティでフォーマットを指定できるので、次のようにする。

```cs:DateTimeFormatConverter.cs
class DateTimeFormatConverter : IsoDateTimeConverter
{
     public DateTimeFormatConverter(string format)
     {
         DateTimeFormat = format;
     }
}
```

あとは、`JsonConverterAttribute`を付けるだけ。フォーマット文字列は第２引数で指定できる。ちなみに、フォーマット文字列は [DateTime](https://docs.microsoft.com/ja-jp/dotnet/standard/base-types/custom-date-and-time-format-strings) のものと同じです。

```cs
class Account
{
    public enum AccountRole { Role1, Role2, Role3 }

    public int ID { get; set; }
    public string Name { get; set; }
    public bool IsActive { get; set; }
    [JsonConverter(typeof(DateTimeFormatConverter), "yyyy/MM/dd HH:mm:ss")]
    public DateTimeOffset CreatedDate { get; set; }
    [JsonConverter(typeof(StringEnumConverter))]
    public AccountRole Role { get; set; }
    public IList<string> Telephones { get; set; } = new List<string>();
}

class Program
{
    static void Main(string[] args)
    {
        // シリアライズ
        var account = new Account()
        {
            ID = 1,
            Name = @"hoge@example.com",
            IsActive = true,
            CreatedDate = DateTimeOffset.Now,
            Role = Account.AccountRole.Role1,
            Telephones = new List<string>()
            {
                "010-1111-2222",
                "020-2222-3333",
            },
        };

        var json = JsonConvert.SerializeObject(account, Formatting.Indented);
        Console.WriteLine(json);

        Console.ReadLine();
    }
}
```

出力

```console
{
  "ID": 1,
  "Name": "hoge@example.com",
  "IsActive": true,
  "CreatedDate": "2020/03/19 14:25:18",
  "Role": "Role1",
  "Telephones": [
    "010-1111-2222",
    "020-2222-3333"
  ]
}
```

## DateTimeのデシリアライズ

ちゃんとソースコードを見てないが、おそらく内部では `DateTime.Parse()` が使われているっぽいので、多少ルーズな書式でも読んでくれる。

```cs
class Account
{
    public enum AccountRole { Role1, Role2, Role3 }

    public int ID { get; set; }
    public string Name { get; set; }
    public bool IsActive { get; set; }
    public DateTimeOffset CreatedDate { get; set; }
    [JsonConverter(typeof(StringEnumConverter))]
    public AccountRole Role { get; set; }
    public IList<string> Telephones { get; set; } = new List<string>();
}

class Program
{
    static void Main(string[] args)
    {
        // デシリアライズ
        var json = $@"{{
""ID"": 999, 
""Name"": ""foo"", 
""IsActive"": true, 
""CreatedDate"": ""2020/01/02 03:04:05"", 
""Role"": ""Role2""
}}";

        var obj = JsonConvert.DeserializeObject<Account>(json);
        Console.WriteLine(string.Join("\n", typeof(Account).GetProperties().Select(info => $"{info.Name}: {info.GetValue(obj)}")));

        Console.ReadLine();
    }
}
```

出力

```console
ID: 999
Name: foo
IsActive: True
CreatedDate: 2020/01/02 3:04:05 +09:00
Role: Role2
Telephones: System.Collections.Generic.List`1[System.String]
```

ちゃんと書式を指定したい場合は、シリアライズで書いたように、`DateTimeFormatConverter` を自作して指定する。
※次のサンプルでは、書式を指定しないと、例外が出ます。

```cs
class Account
{
    public enum AccountRole { Role1, Role2, Role3 }

    public int ID { get; set; }
    public string Name { get; set; }
    public bool IsActive { get; set; }
    [JsonConverter(typeof(DateTimeFormatConverter), "yyyy.MM.dd HH.mm.ss")]
    public DateTimeOffset CreatedDate { get; set; }
    [JsonConverter(typeof(StringEnumConverter))]
    public AccountRole Role { get; set; }
    public IList<string> Telephones { get; set; } = new List<string>();
}

class Program
{
    static void Main(string[] args)
    {
        // デシリアライズ
        var json = $@"{{
""ID"": 999, 
""Name"": ""foo"", 
""IsActive"": true, 
""CreatedDate"": ""2020.01.02 03.04.05"", 
""Role"": ""Role2""
}}";

        var obj = JsonConvert.DeserializeObject<Account>(json);
        Console.WriteLine(string.Join("\n", typeof(Account).GetProperties().Select(info => $"{info.Name}: {info.GetValue(obj)}")));

        Console.ReadLine();
    }
}
```

出力

```console
ID: 999
Name: foo
IsActive: True
CreatedDate: 2020/01/02 3:04:05 +09:00
Role: Role2
Telephones: System.Collections.Generic.List`1[System.String]
```

## ネストクラス

特に何も考える必要はない。

```cs
class Account
{
    public enum AccountRole { Role1, Role2, Role3 }

    public class AccountAddress
    {
        public string Address1 { get; set; }
        public string Address2 { get; set; }
    }

    public int ID { get; set; }
    public string Name { get; set; }
    public bool IsActive { get; set; }
    [JsonConverter(typeof(StringEnumConverter))]
    public DateTimeOffset CreatedDate { get; set; }
    public AccountRole Role { get; set; }
    public IList<string> Telephones { get; set; } = new List<string>();
    public AccountAddress Address { get; set; } = new AccountAddress();
}

class Program
{
    static void Main(string[] args)
    {
        // シリアライズ
        var account = new Account()
        {
            ID = 1,
            Name = @"hoge@example.com",
            IsActive = true,
            CreatedDate = DateTimeOffset.Now,
            Role = Account.AccountRole.Role1,
            Telephones = new List<string>()
            {
                "010-1111-2222",
                "020-2222-3333",
            },
            Address = new Account.AccountAddress()
            {
                 Address1 = "address1",
                 Address2 = "address2",
            },
        };

        var json = JsonConvert.SerializeObject(account, Formatting.Indented);
        Console.WriteLine(json);

        Console.ReadLine();
    }
}
```

出力

```console
{
  "ID": 1,
  "Name": "hoge@example.com",
  "IsActive": true,
  "CreatedDate": "2020-03-19T11:27:59.1366704+09:00",
  "Role": "Role1",
  "Telephones": [
    "010-1111-2222",
    "020-2222-3333"
  ],
  "Address": {
    "Address1": "address1",
    "Address2": "address2"
  }
}
```

# Jsonの属性名を指定する

## 一律ヘビ記法にする

シリアル化するときに、次のように、`DefaultContractResolver`で`SnakeCaseNamingStrategy`を指定して、それを`JsonSerializerSettings`に設定する。

```cs
class Account
{
    public enum AccountRole { Role1, Role2, Role3 }

    public int ID { get; set; }
    public string Name { get; set; }
    public bool IsActive { get; set; }
    public DateTimeOffset CreatedDate { get; set; }
    [JsonConverter(typeof(StringEnumConverter))]
    public AccountRole Role { get; set; }
    public IList<string> Telephones { get; set; } = new List<string>();
}

class Program
{
    static void Main(string[] args)
    {
        // シリアライズ
        var account = new Account()
        {
            ID = 1,
            Name = @"hoge@example.com",
            IsActive = true,
            CreatedDate = DateTimeOffset.Now,
            Role = Account.AccountRole.Role1,
            Telephones = new List<string>()
            {
                "010-1111-2222",
                "020-2222-3333",
            },
        };

        var resolver = new DefaultContractResolver
        {
            NamingStrategy = new SnakeCaseNamingStrategy()
        };

        var json = JsonConvert.SerializeObject(account, new JsonSerializerSettings()
        {
            ContractResolver = resolver,
            Formatting = Formatting.Indented,
        });
        Console.WriteLine(json);

        Console.ReadLine();
    }
}
```

出力

```console
{
  "id": 1,
  "name": "hoge@example.com",
  "is_active": true,
  "created_date": "2020-03-19T11:29:38.0498159+09:00",
  "role": "Role1",
  "telephones": [
    "010-1111-2222",
    "020-2222-3333"
  ]
}
```

## 個別に指定する

`JsonPropertyAttribute`の引数で指定する。

```cs
class Account
{
    public enum AccountRole { Role1, Role2, Role3 }

    [JsonProperty("user_id")]
    public int ID { get; set; }
    [JsonProperty("name")]
    public string Name { get; set; }
    [JsonProperty("active")]
    public bool IsActive { get; set; }
    [JsonProperty("created_date")]
    public DateTimeOffset CreatedDate { get; set; }
    [JsonProperty("role")]
    [JsonConverter(typeof(StringEnumConverter))]
    public AccountRole Role { get; set; }
    [JsonProperty("telepohones")]
    public IList<string> Telephones { get; set; } = new List<string>();
}

class Program
{
    static void Main(string[] args)
    {
        // シリアライズ
        var account = new Account()
        {
            ID = 1,
            Name = @"hoge@example.com",
            IsActive = true,
            CreatedDate = DateTimeOffset.Now,
            Role = Account.AccountRole.Role1,
            Telephones = new List<string>()
            {
                "010-1111-2222",
                "020-2222-3333",
            },
        };

        var json = JsonConvert.SerializeObject(account, Formatting.Indented);
        Console.WriteLine(json);

        Console.ReadLine();
    }
}
```

出力

```console
{
  "user_id": 1,
  "name": "hoge@example.com",
  "active": true,
  "created_date": "2020-03-19T11:34:57.7137179+09:00",
  "role": "Role1",
  "telephones": [
    "010-1111-2222",
    "020-2222-3333"
  ]
}
```

## Jsonに含めない

`JsonIgnoreAttribute`を付ける。

```cs
class Account
{
    public enum AccountRole { Role1, Role2, Role3 }

    [JsonProperty("user_id")]
    public int ID { get; set; }
    [JsonProperty("name")]
    public string Name { get; set; }
    [JsonProperty("active")]
    public bool IsActive { get; set; }
    [JsonProperty("created_date")]
    public DateTimeOffset CreatedDate { get; set; }
    [JsonProperty("role")]
    [JsonConverter(typeof(StringEnumConverter))]
    public AccountRole Role { get; set; }
    [JsonIgnore]
    public IList<string> Telephones { get; set; } = new List<string>();
}

class Program
{
    static void Main(string[] args)
    {
        // シリアライズ
        var account = new Account()
        {
            ID = 1,
            Name = @"hoge@example.com",
            IsActive = true,
            CreatedDate = DateTimeOffset.Now,
            Role = Account.AccountRole.Role1,
            Telephones = new List<string>()
            {
                "010-1111-2222",
                "020-2222-3333",
            },
        };

        var json = JsonConvert.SerializeObject(account, Formatting.Indented);
        Console.WriteLine(json);

        Console.ReadLine();
    }
}
```

出力

```console
{
  "user_id": 1,
  "name": "hoge@example.com",
  "active": true,
  "created_date": "2020-03-19T11:40:14.0203605+09:00",
  "role": "Role1"
}
```

## その他の属性を Dictionary にまとめる

`JsonExtensionDataAttribte`を付ける。
※`[JsonIgnore]`が付いているプロパティでも、`[JsonExtensionData]`が付いているプロパティに含められる。

```cs
class Account
{
    public enum AccountRole { Role1, Role2, Role3 }

    [JsonProperty("user_id")]
    public int ID { get; set; }
    [JsonProperty("name")]
    public string Name { get; set; }
    [JsonProperty("active")]
    public bool IsActive { get; set; }
    [JsonProperty("created_date")]
    public DateTimeOffset CreatedDate { get; set; }
    [JsonProperty("role")]
    [JsonConverter(typeof(StringEnumConverter))]
    public AccountRole Role { get; set; }
    [JsonProperty("telepohones")]
    public IList<string> Telephones { get; set; } = new List<string>();
    [JsonIgnore]
    public string Note { get; set; }
    [JsonExtensionData]
    public IDictionary<string, object> Extra { get; set; } = new Dictionary<string, object>();
}

class Program
{
    static void Main(string[] args)
    {
        // デシリアライズ
        var json = $@"{{
""user_id"": 999, 
""name"": ""foo"", 
""active"": true, 
""created_date"": ""2020/01/02 03:04:05"", 
""role"": ""Role2"", 
""note"": ""メモ"", 
""hoge"": ""fuga""
}}";

        var obj = JsonConvert.DeserializeObject<Account>(json);
        Console.WriteLine($"ID: {obj.ID}");
        Console.WriteLine($"Name: {obj.Name}");
        Console.WriteLine($"IsActive: {obj.IsActive}");
        Console.WriteLine($"CreatedDate: {obj.CreatedDate}");
        Console.WriteLine($"Role: {obj.Role}");
        Console.WriteLine($"Note: {obj.Note}");
        Console.WriteLine($"Extra: {string.Join(", ", obj.Extra.Select(pair => pair))}");

        Console.ReadLine();
    }
}
```

出力

```console
ID: 999
Name: foo
IsActive: True
CreatedDate: 2020/01/02 3:04:05 +09:00
Role: Role2
Note:
Extra: [note, メモ], [hoge, fuga]
```

# JsonをまとめてDictionaryに突っ込む

Jsonが key-value 形式になっていて読み込む場合、いちいちクラスを作るのではなく、`Dictionary` に入れたいときがある。その場合も、`JsonConvert.DeserializeObject<T>` の `T` に `Dictioanry<string, object>` を指定すればいい。

```cs
class Program
{
    static void Main(string[] args)
    {
        // デシリアライズ
        var json = $@"{{
""user_id"": 999, 
""name"": ""foo"", 
""active"": true, 
""created_date"": ""2020/01/02 03:04:05"", 
""role"": ""Role2"",
""telephone"": [""010-1111-2222"", ""020-2222-3333""], 
""address"": {{""address1"":""住所１"", ""address2"":""住所２""}}
}}";

        var obj = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
        Console.WriteLine(string.Join("\n", obj.Select(pair => pair)));

        Console.ReadLine();
    }
}
```

出力

```console
[user_id, 999]
[name, foo]
[active, True]
[created_date, 2020/01/02 03:04:05]
[role, Role2]
[telephone, [
  "010-1111-2222",
  "020-2222-3333"
]]
[address, {
  "address1": "住所１",
  "address2": "住所２"
}]
```

# ストリームから直接読み書き

ファイルや外部のAPIからJsonを読み書きする場合、`JsonSerializer` を使うと、いちいち文字列を経由することなく、直接読み書きができる。

```cs
class Account
{
    public enum AccountRole { Role1, Role2, Role3 }

    public class AccountAddress
    {
        [JsonProperty("address1")]
        public string Address1 { get; set; }
        [JsonProperty("address2")]
        public string Address2 { get; set; }
    }

    [JsonProperty("user_id")]
    public int ID { get; set; }
    [JsonProperty("name")]
    public string Name { get; set; }
    [JsonProperty("active")]
    public bool IsActive { get; set; }
    [JsonProperty("created_date")]
    [JsonConverter(typeof(DateTimeFormatConverter), "yyyy/MM/dd HH:mm:ss")]
    public DateTimeOffset CreatedDate { get; set; }
    [JsonProperty("role")]
    [JsonConverter(typeof(StringEnumConverter))]
    public AccountRole Role { get; set; }
    [JsonProperty("telephones")]
    public IList<string> Telephones { get; set; } = new List<string>();
    [JsonProperty("address")]
    public AccountAddress Address { get; set; } = new AccountAddress();

    [JsonIgnore]
    public string Note { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        var account = new Account()
        {
            ID = 1,
            Name = @"hoge@example.com",
            IsActive = true,
            CreatedDate = DateTimeOffset.Now,
            Role = Account.AccountRole.Role1,
            Address = new Account.AccountAddress()
            {
                Address1 = "address1",
                Address2 = "address2",
            },
            Telephones = new List<string>()
            {
                "010-1111-2222",
                "020-2222-3333",
            },
            Note = "ほげほげ",
        };

        // シリアライズ
        using (StreamWriter writer = File.CreateText(@"account.json"))
        {
            var serializer = new JsonSerializer();
            serializer.Serialize(writer, account);
        }

        // デシリアライズ
        using (StreamReader reader = File.OpenText(@"account.json"))
        {
            var serializer = new JsonSerializer();
            var obj = (Account) serializer.Deserialize(reader, typeof(Account));
            Console.WriteLine($"ID: {obj.ID}");
            Console.WriteLine($"Name: {obj.Name}");
            Console.WriteLine($"IsActive: {obj.IsActive}");
            Console.WriteLine($"CreatedDate: {obj.CreatedDate}");
            Console.WriteLine($"Role: {obj.Role}");
            Console.WriteLine($"Note: {obj.Note}");
            Console.WriteLine($"Telephones: {string.Join(", ", obj.Telephones)}");
            Console.WriteLine($"Addresses: {obj.Address.Address1}, {obj.Address.Address2}");
        }

        Console.ReadLine();
    }
}
```

出力

```console
ID: 1
Name: hoge@example.com
IsActive: True
CreatedDate: 2020/03/27 12:11:03 +09:00
Role: Role1
Note:
Telephones: 010-1111-2222, 020-2222-3333
Addresses: address1, address2
```

json（見やすいように改行とインデントを入れていますが、実際は１行です）

```json
{
  "user_id": 1,
  "name": "hoge@example.com",
  "active": true,
  "created_date": "2020/03/27 12:11:03",
  "role": "Role1",
  "telephones": ["010-1111-2222", "020-2222-3333"],
  "address": {
    "address1":"address1",
    "address2":"address2"
  }
}
```

# まとめ

まだ不足しているパターンがありそうですが、そのときは追記します。

ここに載せた機能以外には、コンストラクタを使ってデシリアライズ、エラーハンドリング、デバッグ出力などがあります。この辺の機能はマニアックなので、書く予定ないですが。

# 参考

- [本家のマニュアル](https://www.newtonsoft.com/json/help/html/Introduction.htm)（英語）