連載
» 2017年09月13日 05時00分 公開

.NET TIPS:JSONデータを作成/解析するには?[C#/VB]

Json.NETを使ってさまざまな形でJSONデータのシリアライズ/デシリアライズを行う方法を説明する。また、.NET Frameworkのみでこれを行う方法も取り上げる。

[山本康彦,BluewaterSoft/Microsoft MVP for Windows Development]
「.NET TIPS」のインデックス

連載目次

 JSON(ジェイソン、JavaScript Object Notation)は、XMLと並んでWebサービスでよく使われるデータ表現形式だ。JSON形式のデータをC#/Visual Basic(以降、VB)で扱うには、シリアライズ/デシリアライズすることになる。C#/VBのオブジェクトをシリアライズしてJSON形式の文字列を得る、あるいは、JSON形式の文字列をデシリアライズしてC#/VBのオブジェクトを得るのである。

 本稿では、JSONにシリアライズ/デシリアライズする方法として、サードパーティー製のJson.NETを使う方法と、.NET Framework標準のAPIを使う方法を紹介する。特定の方法をすぐに知りたいという方は以下のリンクを活用してほしい。

 なお、どちらの方法も.NET Framework 3.5(Visual Studio 2008)で利用できるが、本稿のサンプルコードにはそれより新しい内容も含んでいる。サンプルコードをそのまま試すには、Visual Studio 2015以降が必要である。

 JSON形式については、次の記事が詳しい(本稿ではJSON形式の詳細については取り上げない)。

 JavaScriptでJSON形式のデータをシリアライズ/デシリアライズする方法は、次の.NET TIPSで解説している。


Json.NETでシリアライズ/デシリアライズするには?

 まずNuGetからJson.NETをプロジェクトに導入する(NuGet上では「Newtonsoft.Json」という名前なので注意)。続いて、ソースコードの先頭に名前空間「Newtonsoft.Json」を取り込む宣言を追加する。

 後は、JsonConvertクラス(Newtonsoft.Json名前空間)のSerializeObjectメソッド/DeserializeObjectメソッドを使えば、任意の.NETオブジェクトとJSON形式の文字列との間でシリアライズ/デシリアライズできる(次のコード)。

// シリアライズ − オブジェクトを渡すだけ
string output = JsonConvert.SerializeObject(data);
Console.WriteLine(output);
// 出力例(改行を入れた):
// {"Description":"サンプル","UpdateDate":"2017-09-06T20:24:04.2588057+09:00",
// "Data":{"1":"データ1","2":"データ2"}}

// デシリアライズ − 型引数に復元するオブジェクトの型を指定し、JSON文字列を渡す
var deserialized = JsonConvert.DeserializeObject<SampleData>(output);

' シリアライズ − オブジェクトを渡すだけ
Dim output As String = JsonConvert.SerializeObject(data)
Console.WriteLine(output)
' 出力例(改行を入れた):
' {"Description":"サンプル","UpdateDate":"2017-09-06T20:24:04.2588057+09:00",
' "Data":{"1":"データ1","2":"データ2"}}

' デシリアライズ − 型引数に復元するオブジェクトの型を指定し、JSON文字列を渡す
Dim deserialized = JsonConvert.DeserializeObject(Of SampleData)(output)

Json.NETで、JSON形式文字列との間でシリアライズ/デシリアライズする例(上:C#、下:VB)
SampleData型のオブジェクトdataをシリアライズして文字列outputに変換した。また、その文字列outputをデシリアライズしてSampleData型のオブジェクトdeserializedに復元した。
SampleDataクラスの定義とdata変数の初期化は、すぐ後のコードに掲載する。

 なお、上のコードに登場するSampleDataクラスとその変数dataは次に示すコードのようになっている。

public class SampleData
{
  public string Description { get; set; }
  public DateTimeOffset UpdateDate { get; set; }
  public System.Collections.Generic.Dictionary<int, string> Data { get; set; }
}

……省略……

var data = new SampleData
{
  Description = "サンプル",
  UpdateDate = DateTimeOffset.Now,
  Data = new System.Collections.Generic.Dictionary<int, string>
  {
    {1, "データ1" }, { 2, "データ2"},
  },
};

Public Class SampleData
  Public Property Description As String
  Public Property UpdateDate As DateTimeOffset
  Public Property Data As Dictionary(Of Integer, String)
End Class

……省略……

Dim data = New SampleData With
{
  .Description = "サンプル",
  .UpdateDate = DateTimeOffset.Now,
  .Data = New Dictionary(Of Integer, String) From
  {
    {1, "データ1"}, {2, "データ2"}
  }
}

上のサンプルコードに登場するSampleDataクラスとそのインスタンスdata(上:C#、下:VB)

JSON形式の文字列をキャメルケースで出力するには?(Json.NET)

 先の例で出力された文字列は「{"Description":"サンプル","UpdateDate":"……省略……」となっていて、JSONらしくない。JSONはJavaScript由来の表現形式だから、メンバ名はパスカル形式(=先頭が大文字)ではなくキャメル形式(=先頭が小文字)であってほしいものだ。逆に、キャメル形式で提供されているWebサービスからデシリアライズしたときには、パスカル形式になってほしいのだ。

 また、出力文字列には余分な空白や改行が入っていない。リリース時には通信量を削減する観点から、もちろんそれでいい。しかし、デバッグ時には適宜インデントが入った形式にできれば見やすいだろう。

 そういったさまざまな要望に応えられるオプションが、Json.NETには備わっている。詳しくはJson.NETの公式ドキュメントを参照していただきたい。ここでは、上に挙げた2点を実現してみよう。

 その前に、以降のコードで必要になる名前空間を示しておく(次のコード)。

using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Linq;
using static System.Console;

Imports System.Console
Imports System.IO
Imports System.Text
Imports Newtonsoft.Json
Imports Newtonsoft.Json.Serialization

以下のサンプルコードで使う名前空間の宣言(上:C#、下:VB)

 キャメル形式で、デバッグ時にはインデントの入ったJSON形式文字列を出力するには、次のコードのようにする。デシリアライズするときにはオプション指定が不要な点に注意してほしい(デシリアライズ時にメンバ名の大文字/小文字を区別しない)。また、インデントが付いていても、問題なくデシリアライズされる。

var settings = new JsonSerializerSettings
{
  // メンバ名をキャメルケースで出力するオプション
  ContractResolver = new CamelCasePropertyNamesContractResolver(),
};
#if DEBUG
// 出力をインデントするオプション(リリースビルドでは付けない)
settings.Formatting = Formatting.Indented;
#endif

// オプション指定を付けてシリアライズする
string output = JsonConvert.SerializeObject(data, settings);
WriteLine(output);
// 出力例:
// {
//   "description": "サンプル",
//   "updateDate": "2017-09-06T20:24:04.2588057+09:00",
//   "data": {
//     "1": "データ1",
//     "2": "データ2"
//   }
// }

// デシリアライズ時には上記のオプション指定は不要
var deserialized = JsonConvert.DeserializeObject<SampleData>(output);
WriteLine($"Description={deserialized.Description}");
WriteLine($"UpdateDate={deserialized.UpdateDate}");
WriteLine($"Data[1]={deserialized.Data[1]}");
WriteLine($"Data[2]={deserialized.Data[2]}");
// 出力例:
// Description=サンプル
// UpdateDate=2017/09/06 20:24:04 +09:00
// Data[1]=データ1
// Data[2]=データ2

' メンバ名をキャメルケースで出力するオプション
Dim settings = New JsonSerializerSettings With
{
  .ContractResolver = New CamelCasePropertyNamesContractResolver()
}
#If DEBUG Then
' 出力をインデントするオプション(リリースビルドでは付けない)
settings.Formatting = Formatting.Indented
#End If

' オプション指定を付けてシリアライズする
Dim output As String = JsonConvert.SerializeObject(data, settings)
WriteLine(output)
' 出力例:
' {
'   "description": "サンプル",
'   "updateDate": "2017-09-06T20:24:04.2588057+09:00",
'   "data": {
'     "1": "データ1",
'     "2": "データ2"
'   }
' }

' デシリアライズ時には上記のオプション指定は不要
Dim deserialized = JsonConvert.DeserializeObject(Of SampleData)(output)
WriteLine($"Description={deserialized.Description}")
WriteLine($"UpdateDate={deserialized.UpdateDate}")
WriteLine($"Data[1]={deserialized.Data(1)}")
WriteLine($"Data[2]={deserialized.Data(2)}")
' 出力例:
' Description=サンプル
' UpdateDate=2017/09/06 20:24:04 +09:00
' Data[1]=データ1
' Data[2]=データ2

Json.NETで、キャメル形式+インデントの入ったJSON形式文字列を出力する例(上:C#、下:VB)
インデントされた見やすい出力で確認しておいていただきたいのだが、DateTimeOffset型は時差の付いた時刻表示文字列になっている。Dictionary<tkey,tvalue>型は、{Key:Value}というオブジェクトを複数持ったオブジェクトにシリアライズされている。本稿の最後で紹介する.NET Framework標準APIのDataContractJsonSerializerクラス(System.Runtime.Serialization.Json名前空間)によるシリアライズ結果と比較してほしい。

異なる型にデシリアライズするには?(Json.NET)

 WebサービスのJSON形式データを取り込むとき、その一部分だけが欲しいときがある。例えば、100個のメンバが送られてくるが、アプリで必要なのはそのうちの10個だけだといった場合だ。そんなときも、使わない残り90個を含めたクラス定義を書かなければならないのだろうか?

 あるいは、Webサービスに仕様変更があって、送られてくるメンバが増えたとする。それを利用しているアプリでは、即座にクラス定義を修正したバージョンをリリースしなければならないのだろうか?

 以上の問題は、異なる型にデシリアライズできれば解決する。Json.NETは、デシリアライズ時に一致しないメンバを無視するので(例外を出さないので)、型が違っていてもデシリアライズ可能なのだ(次のコード)。

public class SampleData2
{
  // 先のSampleDataと比べて、DescriptionとUpdateDateがない
  public Dictionary<int, string> Data { get; set; }
  // Commentが余分にある
  public string Comment { get; set; }
}

……省略……

// SampleData型をシリアライズ(冒頭のサンプルコードと同じ)
string output = JsonConvert.SerializeObject(data);

// SampleData2型にデシリアライズ
var anotherType = JsonConvert.DeserializeObject<SampleData2>(output);
WriteLine($"Data[1]={anotherType.Data[1]}");
WriteLine($"Data[2]={anotherType.Data[2]}");
WriteLine($"Comment={anotherType.Comment??"(null)"}");
// 出力:
// Data[1]=データ1
// Data[2]=データ2
// Comment=(null)

Public Class SampleData2
  ' 先のSampleDataと比べて、DescriptionとUpdateDateがない
  Public Property Data As Dictionary(Of Integer, String)
  ' Commentが余分にある
  Public Property Comment As String
End Class

……省略……

' SampleData型をシリアライズ(冒頭のサンプルコードと同じ)
Dim output As String = JsonConvert.SerializeObject(data)

' SampleData2型にデシリアライズ
Dim anotherType = JsonConvert.DeserializeObject(Of SampleData2)(output)
WriteLine($"Data[1]={anotherType.Data(1)}")
WriteLine($"Data[2]={anotherType.Data(2)}")
WriteLine($"Comment={If(anotherType.Comment, "(null)")}")
' 出力:
' Data[1]=データ1
' Data[2]=データ2
' Comment=(null)

Json.NETで、異なる型にデシリアライズする例(上:C#、下:VB)
シリアライズ前とデシリアライズ後の型が違っていても、Json.NETは、一致するメンバだけを復元してくれる。Webサービスを利用するときに、とても都合がよい。なお、後ほど紹介するDataContractJsonSerializerクラスでも同様である。

List<T>をシリアライズすると?(Json.NET)

 先のサンプルコードで、Dictionary<TKey,TValue>クラスは{Key:Value}というオブジェクトを複数持ったオブジェクトにシリアライズされた。それ以外の一般的なコレクション(List<T>クラスなど)は、配列にシリアライズされる(次のコード)。

var src = new List<string>{ "abc", "123"};

var settings = new JsonSerializerSettings();
#if DEBUG
settings.Formatting = Formatting.Indented;
#endif

string output = JsonConvert.SerializeObject(src, settings);
WriteLine(output);
// 出力:
// [
//   "abc",
//   "123"
// ]

var deserialized = JsonConvert.DeserializeObject<List<string>>(output);
WriteLine($"{string.Join(", ", deserialized)}");
// 出力:
// abc, 123

Dim src = New List(Of String) From {"abc", "123"}

Dim settings = New JsonSerializerSettings()
#If DEBUG Then
settings.Formatting = Formatting.Indented
#End If

Dim output As String = JsonConvert.SerializeObject(src, settings)
WriteLine(output)
' 出力:
' [
'   "abc",
'   "123"
' ]

Dim deserialized = JsonConvert.DeserializeObject(Of List(Of String))(output)
WriteLine($"{String.Join(", ", deserialized)}")
' 出力:
' abc, 123

Json.NETで、List<T>型をシリアライズ/デシリアライズする例(上:C#、下:VB)
List<T>型などの一般的なコレクションは、JSON形式では配列になる。JSON形式で「[]」で囲まれた部分が配列だ。

ストリームに対してシリアライズ/デシリアライズするには?(Json.NET)

 Json.NETは、文字列に対してだけでなく、ストリームに対してもシリアライズ/デシリアライズできる。例えば、Webサービスから受信したJSON形式データを文字列としてメモリ上にためることなく、受信ストリームから直接オブジェクトにデシリアライズできるのだ。

 ストリームに対してシリアライズ/デシリアライズするには、JsonSerializerクラス/JsonTextWriterクラス/JsonTextReaderクラスを使う。MemoryStreamに対して行う例を次のコードに示す。

var serializer = new JsonSerializer();
// オプション指定は、JsonSerializerオブジェクトの各プロパティで行う
serializer.ContractResolver = new CamelCasePropertyNamesContractResolver();
#if DEBUG
serializer.Formatting = Formatting.Indented;
#endif

string output;

// シリアライズ
using (var ms = new MemoryStream())
using (var sw = new StreamWriter(ms)) // 書き込み用のストリームを用意し、
using (var writer = new JsonTextWriter(sw)) // JsonTextWriterオブジェクトを作り、
{
  // JsonSerializerオブジェクトのSerializeメソッドに、
  // JsonTextWriterオブジェクトとシリアライズしたいオブジェクトを渡す
  serializer.Serialize(writer, data);

  // 出力を確認
  writer.Flush();
  output = Encoding.UTF8.GetString(ms.ToArray()); // 既定ではUTF-8で出力
  WriteLine(output);
  // 出力例:
  // {
  //   "description": "サンプル",
  //   "updateDate": "2017-09-06T20:24:04.2588057+09:00",
  //   "data": {
  //     "1": "データ1",
  //     "2": "データ2"
  //   }
  // }
}

// デシリアライズ
SampleData deserialized;
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(output),false))
using (var sr = new StreamReader(ms)) // 読み込み用のストリームを開き、
using (var reader = new JsonTextReader(sr)) // JsonTextReaderオブジェクトを作り、
{
  // JsonSerializerオブジェクトのDeserializeメソッドに、
  // JsonTextReaderオブジェクトを渡す
  deserialized = serializer.Deserialize<SampleData>(reader);
}
WriteLine($"Description={deserialized.Description}");
WriteLine($"UpdateDate={deserialized.UpdateDate}");
WriteLine($"Data[1]={deserialized.Data[1]}");
WriteLine($"Data[2]={deserialized.Data[2]}");
// 出力例:
// Description=サンプル
// UpdateDate=2017/09/06 20:24:04 +09:00
// Data[1]=データ1
// Data[2]=データ2

Dim serializer = New JsonSerializer()
' オプション指定は、JsonSerializerオブジェクトの各プロパティで行う
serializer.ContractResolver = New CamelCasePropertyNamesContractResolver()
#If DEBUG Then
serializer.Formatting = Formatting.Indented
#End If

Dim output As String

' シリアライズ
Using ms = New MemoryStream()
  Using sw = New StreamWriter(ms) ' 書き込み用のストリームを用意し、
    Using writer = New JsonTextWriter(sw) ' JsonTextWriterオブジェクトを作り、
      ' JsonSerializerオブジェクトのSerializeメソッドに、
      ' JsonTextWriterオブジェクトとシリアライズしたいオブジェクトを渡す
      serializer.Serialize(writer, data)

      '出力を確認
      writer.Flush()
      output = Encoding.UTF8.GetString(ms.ToArray()) ' 既定ではUTF-8で出力
      WriteLine(output)
      ' 出力例:
      ' {
      '   "description": "サンプル",
      '   "updateDate": "2017-09-06T20:24:04.2588057+09:00",
      '   "data": {
      '     "1": "データ1",
      '     "2": "データ2"
      '   }
      ' }
    End Using
  End Using
End Using

' デシリアライズ
Dim deserialized As SampleData
Using ms = New MemoryStream(Encoding.UTF8.GetBytes(output), False)
  Using sr = New StreamReader(ms) ' 読み込み用のストリームを開き、
    Using reader = New JsonTextReader(sr) ' JsonTextReaderオブジェクトを作り、
      ' JsonSerializerオブジェクトのDeserializeメソッドに、
      ' JsonTextReaderオブジェクトを渡す
      deserialized = serializer.Deserialize(Of SampleData)(reader)
    End Using
  End Using
End Using
WriteLine($"Description={deserialized.Description}")
WriteLine($"UpdateDate={deserialized.UpdateDate}")
WriteLine($"Data[1]={deserialized.Data(1)}")
WriteLine($"Data[2]={deserialized.Data(2)}")
' 出力例:
' Description=サンプル
' UpdateDate=2017/09/06 20:24:04 +09:00
' Data[1]=データ1
' Data[2]=データ2

Json.NETで、MemoryStreamに対してシリアライズ/デシリアライズする例(上:C#、下:VB)

型を定義せずにデシリアライズするには?(Json.NET)

 Json.NETでは、デシリアライズの型としてdynamic(C#)/Object(VB)を指定できる。すなわち、クラス定義を書かなくてもデシリアライズできるのだ(次のコード)。欲しいメンバがプリミティブな型だけならば、とても有効な方法だ。メンバに複雑な型があるときは、次のコードにあるように少々面倒なコードが必要になる。

var settings = new JsonSerializerSettings
{
  // メンバ名をキャメルケースで出力するオプション
  ContractResolver = new CamelCasePropertyNamesContractResolver(),
};
#if DEBUG
settings.Formatting = Formatting.Indented;
#endif

// SampleData型をキャメル形式にシリアライズ
string output = JsonConvert.SerializeObject(data, settings);

// dynamic型にデシリアライズ
dynamic obj = JsonConvert.DeserializeObject<dynamic>(output);
WriteLine($"description={obj.description}");
WriteLine($"updateDate={obj.updateDate}");
foreach(var p in (obj.data as Newtonsoft.Json.Linq.JContainer)
                  .Select(token => token as Newtonsoft.Json.Linq.JProperty))
  WriteLine($"data[{p.Name}]={p.Value}");
// 出力例:
// description=サンプル
// updateDate=2017/09/07 11:36:12
// data[1]=データ1
// data[2]=データ2

' メンバ名をキャメルケースで出力するオプション
Dim settings = New JsonSerializerSettings With
{
  .ContractResolver = New CamelCasePropertyNamesContractResolver()
}
#If DEBUG Then
settings.Formatting = Formatting.Indented
#End If

' Option Strict Offの指定が必要
' SampleData型をキャメル形式にシリアライズ
Dim output As String = JsonConvert.SerializeObject(data, settings)

' Object型にデシリアライズ
Dim obj As Object = JsonConvert.DeserializeObject(Of Object)(output)
WriteLine($"description={obj("description")}")
WriteLine($"updateDate={obj("updateDate")}")
For Each p In TryCast(obj("data"), Newtonsoft.Json.Linq.JContainer) _
          .Select(Function(token) TryCast(token, Newtonsoft.Json.Linq.JProperty))
  WriteLine($"data[{p.Name}]={p.Value}")
Next
' 出力例:
' description=サンプル
' updateDate=2017/09/07 11:36:12
' data[1]=データ1
' data[2]=データ2

Json.NETで、型を定義せずにデシリアライズする例(上:C#、下:VB)

 キャメルケースにシリアライズして、それを動的な型にデシリアライズしていることから、メンバを参照する際にはその名前が大文字ではなく小文字で始まっている点には注意しよう。

.NET Framework標準のAPIでJSON形式にシリアライズ/デシリアライズするには?

 最後に、どうしてもサードパーティー製のライブラリは使いたくないという場合のために、標準の.NET Framework APIで行う方法を紹介しておこう。

 それには、DataContractJsonSerializerクラス(System.Runtime.Serialization.Json名前空間)を使えばよい。

 DataContractJsonSerializerクラスを使うには、プロジェクトの参照にSystem.Runtime.Serializationを追加する必要がある。

 DataContractJsonSerializerクラスでシリアライズ/デシリアライズするには、その対象となるクラスにDataContract属性、対象となるメンバにDataMember属性を付ける(次のコード)。

[System.Runtime.Serialization.DataContract]
public class SampleData3
{
  [System.Runtime.Serialization.DataMember]
  public string Description { get; set; }

  [System.Runtime.Serialization.DataMember]
  public DateTimeOffset UpdateDate { get; set; }

  [System.Runtime.Serialization.DataMember]
  public DateTime UpdateDate2 { get; set; } // DateTimeOffsetとの比較用

  [System.Runtime.Serialization.DataMember]
  public Dictionary<int, string> Data { get; set; }
}

<System.Runtime.Serialization.DataContract>
Public Class SampleData3
  <System.Runtime.Serialization.DataMember>
  Public Property Description As String

  <System.Runtime.Serialization.DataMember>
  Public Property UpdateDate As DateTimeOffset

  <System.Runtime.Serialization.DataMember>
  Public Property UpdateDate2 As DateTime ' DateTimeOffsetとの比較用

  <System.Runtime.Serialization.DataMember>
  Public Property Data As Dictionary(Of Integer, String)
End Class

DataContractJsonSerializerクラスで扱える対象の例(上:C#、下:VB)
シリアライズ/デシリアライズしたいクラスにDataContract属性を、シリアライズ/デシリアライズしたいメンバにDataMember属性を付ける。

 そうしたら、次のコードのようにしてシリアライズ/デシリアライズする。次のコードにあるように、DataContractJsonSerializerSettingsオブジェクトを使ってある程度は書式設定などが可能だ。

// シリアライズするデータ
var data2 = new SampleData3
{
  Description = "サンプル",
  UpdateDate = DateTimeOffset.Now,
  UpdateDate2 = DateTime.Now,
  Data = new Dictionary<int, string>
  {
    {1, "データ1" }, { 2, "データ2"},
  },
};

// 日付出力時の書式設定
var settings
  = new System.Runtime.Serialization.Json.DataContractJsonSerializerSettings
    { 
      DateTimeFormat 
      = new System.Runtime.Serialization.DateTimeFormat("yyyy-MM-dd'T'HH:mm:ssK")
    };

string output;

// シリアライザーを用意する
var sw = new System.Runtime.Serialization.Json
              .DataContractJsonSerializer(typeof(SampleData3), settings);

// シリアライズ
using (var ms = new MemoryStream()) // 書き込み用のストリームを用意し、
{
  // シリアライザーのWriteObjectメソッドにストリームと対象オブジェクトを渡す
  sw.WriteObject(ms, data2);

  output = Encoding.UTF8.GetString(ms.ToArray()); // 既定ではUTF-8で出力
  WriteLine(output);
  // 出力例(適宜改行とインデントを入れた):
  // {
  //   "Data":
  //   [
  //     {"Key":1,"Value":"データ1"},
  //     {"Key":2,"Value":"データ2"}
  //   ],
  //   "Description":"サンプル",
  //   "UpdateDate":{"DateTime":"2017-09-06T11:24:04Z","OffsetMinutes":540},
  //   "UpdateDate2":"2017-09-06T20:24:04+09:00"
  // }
  // 参考:DateTimeFormatの指定をしないと、UpdateDateは次のようになる
  // "UpdateDate":{"DateTime":"\/Date(1504739100997)\/","OffsetMinutes":540}
}

// デシリアライズ
SampleData3 deserialized;
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(output), false))
{
  // シリアライザーのReadObjectメソッドに読み取りストリームを渡す
  deserialized = sw.ReadObject(ms) as SampleData3;
}
WriteLine($"Description={deserialized.Description}");
WriteLine($"UpdateDate={deserialized.UpdateDate}");
WriteLine($"UpdateDate2={deserialized.UpdateDate2}");
WriteLine($"Data[1]={deserialized.Data[1]}");
WriteLine($"Data[2]={deserialized.Data[2]}");
// 出力例:
// Description=サンプル
// UpdateDate=2017/09/06 20:24:04 +09:00
// UpdateDate2=2017/09/06 20:24:04
// Data[1]=データ1
// Data[2]=データ2

' シリアライズするデータ
Dim data2 = New SampleData3 With
{
  .Description = "サンプル",
  .UpdateDate = DateTimeOffset.Now,
  .UpdateDate2 = DateTime.Now,
  .Data = New Dictionary(Of Integer, String) From
    {
      {1, "データ1"}, {2, "データ2"}
    }
}

' 日付出力時の書式設定
Dim settings _
  = New System.Runtime.Serialization.Json.DataContractJsonSerializerSettings With
    {
      .DateTimeFormat _
      = New System.Runtime.Serialization.DateTimeFormat("yyyy-MM-dd'T'HH:mm:ssK")
    }

Dim output As String

' シリアライザーを用意する
Dim sw = New System.Runtime.Serialization.Json _
          .DataContractJsonSerializer(GetType(SampleData3), settings)

' シリアライズ
Using ms = New MemoryStream() ' 書き込み用のストリームを用意し、
  ' シリアライザーのWriteObjectメソッドにストリームと対象オブジェクトを渡す
  sw.WriteObject(ms, data2)

  output = Encoding.UTF8.GetString(ms.ToArray()) ' 既定ではUTF-8で出力
  WriteLine(output)
  ' 出力例(適宜改行とインデントを入れた):
  ' {
  '   "Data":
  '   [
  '     {"Key":1,"Value":"データ1"},
  '     {"Key":2,"Value":"データ2"}
  '   ],
  '   "Description":"サンプル",
  '   "UpdateDate":{"DateTime":"2017-09-06T11:24:04Z","OffsetMinutes":540},
  '   "UpdateDate2":"2017-09-06T20:24:04+09:00"
  ' }
  ' 参考:DateTimeFormatの指定をしないと、UpdateDateは次のようになる
  ' "UpdateDate":{"DateTime":"\/Date(1504739100997)\/","OffsetMinutes":540}
End Using

' デシリアライズ
Dim deserialized As SampleData3
Using ms = New MemoryStream(Encoding.UTF8.GetBytes(output), False)
  ' シリアライザーのReadObjectメソッドに読み取りストリームを渡す
  deserialized = sw.ReadObject(ms)
End Using
WriteLine($"Description={deserialized.Description}")
WriteLine($"UpdateDate={deserialized.UpdateDate}")
WriteLine($"UpdateDate2={deserialized.UpdateDate2}")
WriteLine($"Data[1]={deserialized.Data(1)}")
WriteLine($"Data[2]={deserialized.Data(2)}")
' 出力例:
' Description=サンプル
' UpdateDate=2017/09/06 20:24:04 +09:00
' UpdateDate2=2017/09/06 20:24:04
' Data[1]=データ1
' Data[2]=データ2

DataContractJsonSerializerクラスでシリアライズ/デシリアライズする例(上:C#、下:VB)
Json.NETと比べると、Dictionary<tkey,tvalue>型のJSON出力に、いちいち「Key」/「Value」と入っていて冗長だ。また、DateTimeOffset型(メンバ「UpdateDate」)のJSON出力は、クロスプラットフォームで使うのが不安に思えてしまう。

まとめ

 JSON形式のデータを作ったり解析したりするには、サードパーティー製のJson.NETを使うのが定番だ。シリアライズ/デシリアライズする対象のクラスに手を入れなくて済むし、柔軟にJSON出力を制御できる。

利用可能バージョン:.NET Framework 3.5以降(Visual Studio 2008以降)
カテゴリ:クラスライブラリ 処理対象:文字列
カテゴリ:クラスライブラリ 処理対象:データ
使用ライブラリ: JsonConvertクラス(Newtonsoft.Json名前空間)
使用ライブラリ: JsonSerializerクラス(Newtonsoft.Json名前空間)
使用ライブラリ: JsonTextWriterクラス(Newtonsoft.Json名前空間)
使用ライブラリ: JsonTextReaderクラス(Newtonsoft.Json名前空間)
使用ライブラリ: JsonSerializerSettingsクラス(Newtonsoft.Json.Serialization名前空間)
使用ライブラリ: DataContract属性(System.Runtime.Serialization名前空間)
使用ライブラリ: DataMember属性(System.Runtime.Serialization名前空間)
使用ライブラリ: DataContractJsonSerializerSettingsクラス(System.Runtime.Serialization.Json名前空間)
使用ライブラリ: DataContractJsonSerializerクラス(System.Runtime.Serialization.Json名前空間)
関連TIPS:JSONデータを解析するには?[JavaScript/jQuery]
関連TIPS:JSON形式のデータの内容を確認するには?(JSON Viewer活用)
関連TIPS:バイト列を文字列に変換するには?
関連TIPS:構文:クラス名を書かずに静的メソッドを呼び出すには?[C# 6.0]
関連TIPS:VB.NETでクラス名を省略してメソッドや定数を利用するには?
関連TIPS:数値を右詰めや0埋めで文字列化するには?[C#、VB]
関連TIPS:Visual Studioでコンソール・アプリケーションのデバッグ実行時にコマンド・プロンプトを閉じないようにするには?


「.NET TIPS」のインデックス

.NET TIPS

Copyright© 1999-2017 Digital Advantage Corp. All Rights Reserved.

@IT Special

- PR -

TechTargetジャパン

この記事に関連するホワイトペーパー

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。