雑記
 

VB/C#でJSONの生成 (JSON.NET編)

2020/6/7

この記事が対象とする製品・バージョン

VS2019 Visual Studio 2019 対象です。
VS2017 Visual Studio 2017 対象です。
VS2015 Visual Studio 2015 対象です。
VS2013 Visual Studio 2013 対象ですが、サンプルの一部は動作しません。
VS2012 Visual Studio 2012 対象ですが、サンプルの一部は動作しません。
VS2010 Visual Studio 2010 対象ですが、サンプルの一部は動作しません。
VS2008 Visual Studio 2008 対象ですが、サンプルの一部は動作しません。
VS2005 Visual Studio 2005 対象ですが、サンプルの一部は動作しません。
VS.NET 2003 Visual Studio 2003 × 対象外です。
VS.NET 2002 Visual Studio (2002) × 対象外です。

目次

1.概要

この記事では JSON.NET を扱います。

VB/C#でJSONを読み書きするには、JSON.NET(Newtonsoft JSON)またはSystem.Text.Jsonを使用するのが一般的です。

JSON.NETは非常に人気のあるライブラリで多機能です。あまりにもよくできていたためマイクロソフトのASP.NET CoreですらこのJSON.NETに依存する状況が発生してしまいました。後になってマイクロソフトはこの状況を脱却し、よりパフォーマンスの良い(その代わり機能の少ない)ライブラリを開発しました。それがSystem.Text.Jsonです。どちらを使っても通常の使用であれば機能・パフォーマンスともに申し分ありません。

この記事ではJSON.NETを扱います。

参考

JSONの仕様

VB/C#でJSONの読み込み(JSON.NET編)

↑この記事ではJSON.NETの基本的な考え方も説明しているので、少し複雑なことをやりたい場合は参考にしてください。

 

VB/C#でJSONの読み込み(System.Text.Json編)

 

2.準備

JSON.NET(Newtonsoft JSON)は、プロジェクトの種類によってははじめから使えるようになっていますが、WindowsフォームアプリケーションなどではNuGetからインストールする必要があります。

NuGetからインストールする場合 Newtonsoft.Json  で検索します。かなりメジャーなパッケージなので検索しなくてもはじめから一番上に表示されているかもしれません。

 

JSON.NETでJSONを生成する方法はいくつかあります。以下でいくつか紹介していきます。

 

方法1:クラスをJSONにシリアライズする

JSONで表現したい内容をクラスで表現できるのであれば、JsonConvertクラスのSerializeObjectメソッドを使って簡単にJSONを生成できます。

既定ではPublicなプロパティ・フィールドが出力対象です。ただし既定では静的メンバー(定数、VBのShared、C#のstatic)は出力対象外です。

既定の設定はJsonProperty属性やJsonIgnore属性で簡単に変更できるほか、ContractResolverを使った上級者向けの設定も可能です。属性を使った例はこの後で示します。

JSONにシリアライズされるときにはクラス・プロパティ・フィールドの名前がそのまま使われます。これも変更する簡単な方法は属性を使うことです。クラスの場合JsonObject属性、プロパティ・フィールドの場合JsonProperty属性です。下記の例ではProductImageクラスと、ProductImageThumbnailクラスにJsonObject属性をつけてJSON化したときの名前を指定しています。

一番外側を表現するクラスだけは名前は出力されません。名前なしの { } になります。

なお、JSONの文字列が手元にある場合は、コピー&貼り付けを使って、 Visual Studioの編集メニューから[形式を選択して貼り付け] - [JSONをクラスとして貼り付ける]を選択すると、JSONをVB/C#のクラスに変換して貼り付けてくれるのでこの手のクラスを定義する際に便利です。ただし、思ったように変換してくれない部分を手で修正する必要がある場合もよくあります。

VB

VB2010対応 VB2012対応 VB2013対応 VB2015対応 VB2017対応 VB2019対応

Public Class ProductInfo
    Public Property Image As ProductImage
End Class

<JsonObject("Image")>
Public Class ProductImage
    Public Property OuterWidth As Integer
    Public Property OuterHeight As Integer
    Public Property Title As String
    Public Property Thumbnail As ProductImageThumbnail
    Public Property Animated As Boolean
    Public Property IDs As Integer()
End Class

<JsonObject("Thumbnail")>
Public Class ProductImageThumbnail
    Public Property ImageUrl As String
    Public Property Height As Integer
    Public Property Width As Integer
End Class

Public Sub WriteJson()

    Dim productInfo As New ProductInfo
    Dim image As New ProductImage
    Dim thumbnail As New ProductImageThumbnail

    productInfo.Image = image
    image.OuterWidth = 800
    image.OuterHeight = 600
    image.Title = "15階からの眺望"
    image.Thumbnail = thumbnail
    thumbnail.ImageUrl = "http://www.example.com/image/481989943"
    thumbnail.Height = 125
    thumbnail.Width = 100
    image.Animated = False
    image.IDs = {116, 943, 234, 38793}

    Dim jsonText As String = JsonConvert.SerializeObject(productInfo, Formatting.Indented)

    Debug.WriteLine(jsonText)

End Sub

C#

public class ProductInfo {
    public ProductImage Image { get; set; }
}

[JsonObject("Image")]
public class ProductImage {
    public int OuterWidth { get; set; }
    public int OuterHeight { get; set; }
    public string Title { get; set; }
    public ProductImageThumbnail Thumbnail { get; set; }
    public bool Animated { get; set; }
    public int[] IDs { get; set; }
}

[JsonObject("Thumbnail")]
public class ProductImageThumbnail {
    public string ImageUrl { get; set; }
    public int Height { get; set; }
    public int Width { get; set; }
}

public void WriteJson() {

    ProductInfo productInfo = new ProductInfo ();
    ProductImage image = new ProductImage ();
    ProductImageThumbnail thumbnail = new ProductImageThumbnail ();

    productInfo.Image = image;
    image.OuterWidth = 800;
    image.OuterHeight = 600;
    image.Title = "15階からの眺望";
    image.Thumbnail = thumbnail;
    thumbnail.ImageUrl = "http://www.example.com/image/481989943";
    thumbnail.Height = 125;
    thumbnail.Width = 100;
    image.Animated = false;
    image.IDs = new int[] { 116, 943, 234, 38793 };

    string jsonText = JsonConvert.SerializeObject(productInfo, Formatting.Indented);

    System.Diagnostics.Debug.WriteLine(jsonText);

}

 

このプログラムのWriteJsonメソッドを実行すると出力ウィンドウには次のように出力されます。

{
  "Image": {
    "OuterWidth": 800,
    "OuterHeight": 600,
    "Title": "15階からの眺望",
    "Thumbnail": {
      "ImageUrl": "http://www.example.com/image/481989943",
      "Height": 125,
      "Width": 100
    },
    "Animated": false,
    "IDs": [
      116,
      943,
      234,
      38793
    ]
  }
}

SerializeObjectの第2引数にFormatting.Indentedを指定しているのでこのように、インデント付きで改行されて出力されます。この引数を省略し、  JsonConvert.SerializeObject(productInfo) と記述すると、改行やインデントなしで次のように1行だけ出力されます。(ブラウザーの幅が十分広くない場合、表示上折り返されて2行、3行に見えるかもしれません。)


{"Image":{"OuterWidth":800,"OuterHeight":600,"Title":"15階からの眺望","Thumbnail":{"ImageUrl":"http://www.example.com/image/481989943","Height":125,"Width":100},"Animated":false,"IDs":[116,943,234,38793]}}

 

上述したように 既定ではクラスで定義されている非静的なPublicなプロパティ・フィールドがJSONとして出力されます。基底クラスのプロパティ・フィールドも含みます。

プロパティ・フィールドにJsonIgnore属性をつけるとPublicなものでも出力されなくなります。

プロパティ・フィールドにJsonProperty属性をつけるとPublicでないものや静的なものでも出力されます。Constで宣言されているフィールドもJsonProperty属性をつけると出力されます。

VB

VB2010対応 VB2012対応 VB2013対応 VB2015対応 VB2017対応 VB2019対応

Public Class JsonRoot

    Public Property PublicProp As String = "出力されます"

    <JsonProperty("ChangeName")> '←これがあると出力される名前が変わります。
    Public Property NamedPublicProp As String = "名前が変わって出力されます"

    <JsonIgnore> '←これがあるとPublicプロパティでも出力されなくなります。
    Public Property IgnoredPublicProp As String = "出力されません"

    Private Property PrivateProp As String = "出力されません"

    <JsonProperty> '←これがあるとPrivateプロパティでも出力されるようになります。
    Private Property AttrPrivateProp As String = "出力されます"

    Public PublicField As String = "出力されます"
    Dim PrivateField As String = "出力されません"
    Public Const PublicConst As String = "出力されません"

End Class

Public Sub WriteJson()

    Dim productInfo As New JsonRoot
    Dim jsonText As String = JsonConvert.SerializeObject(productInfo, Formatting.Indented)

    Debug.WriteLine(jsonText)

End Sub

C#

public class JsonRoot
{
    public string PublicProp { get; set; } = "出力されます";

    [JsonProperty("ChangeName")] //←これがあると出力される名前が変わります。
    public string NamedPublicProp { get; set; } = "名前が変わって出力されます";

    [JsonIgnore] //←これがあるとPublicプロパティでも出力されなくなります。
    public string IgnoredPublicProp { get; set; } = "出力されません";

    private string PrivateProp { get; set; } = "出力されません";

    [JsonProperty] //←これがあるとPrivateプロパティでも出力されるようになります。
    private string AttrPrivateProp { get; set; } = "出力されます";

    public string PublicField { get; set; } = "出力されます";
    string PrivateField = "出力されません";
    public const string PublicConst = "出力されません";
}


public void WriteJson()
{
    JsonRoot productInfo = new JsonRoot();
    string jsonText = JsonConvert.SerializeObject(productInfo, Formatting.Indented);

    System.Diagnostics.Debug.WriteLine(jsonText);
}

この例を実行すると出力ウィンドウには次のように出力されます。

{
  "PublicField": "出力されます",
  "PublicProp": "出力されます",
  "ChangeName": "名前が変わって出力されます",
  "AttrPrivateProp": "出力されます"
}

 

既定の動作を変更して対象となるメンバーを指定するにはカスタムコンバーターを利用するか、DefaultContractResolverを継承してGetSerializableMembersメソッドとCreatePropertyメソッドをオーバーライドして使用します。

 

方法2:オブジェクトやプロパティを1つずつJSONとして書き込む

JSONのプロパティ名を書いて、オブジェクトを開始して、プロパティ名を書いて、値を書いて、オブジェクトを終了して・・・と逐一命令してJSONを生成することができます。

VB

VB2005対応 VB2008対応 VB2010対応 VB2012対応 VB2013対応 VB2015対応 VB2017対応 VB2019対応

Dim sb As New System.Text.StringBuilder

Using sw = New IO.StringWriter(sb)
    Using writer As New JsonTextWriter(sw)

        writer.Formatting = Formatting.Indented '改行とインデントをつけます。

        writer.WriteStartObject()             '{
        writer.WritePropertyName("Team")      ' "Team":
        writer.WriteStartObject()             '  {
        writer.WritePropertyName("TeamName"'    "TeamName":
        writer.WriteValue("公安9課")         '    "公安9課"
        writer.WritePropertyName("Authority") '    "Authority":
        writer.WriteValue("内務省")           '    "内務省"
        writer.WritePropertyName("Leader")    '    "Leader":
        writer.WriteStartObject()             '    {
        writer.WritePropertyName("Name")      '      "Name":
        writer.WriteValue("草薙素子")         '      "草薙素子"
        writer.WriteEndObject()               '    }
        writer.WritePropertyName("IDs")       '    "IDs":
        writer.WriteStartArray()              '    [
        writer.WriteValue(123)                '      123,
        writer.WriteValue(456)                '      456
        writer.WriteEndArray()                '    ]
        writer.WriteEndObject()               '  }
        writer.WriteEndObject()               '}

    End Using
End Using

Dim jsonText As String = sb.ToString

Debug.WriteLine(jsonText)

C#

StringBuilder sb = new StringBuilder();

using (var sw = new System.IO.StringWriter(sb))
{
    using (var writer = new JsonTextWriter(sw))
    {
        writer.Formatting = Formatting.Indented; //改行とインデントをつけます。

        writer.WriteStartObject();             //{
        writer.WritePropertyName("Team");      // "Team":
        writer.WriteStartObject();             //  {
        writer.WritePropertyName("TeamName");  //    "TeamName":
        writer.WriteValue("公安9課");         //    "公安9課"
        writer.WritePropertyName("Authority"); //    "Authority":
        writer.WriteValue("内務省");           //    "内務省"
        writer.WritePropertyName("Leader");    //    "Leader":
        writer.WriteStartObject();             //    {
        writer.WritePropertyName("Name");      //      "Name":
        writer.WriteValue("草薙素子");         //      "草薙素子"
        writer.WriteEndObject();               //    }
        writer.WritePropertyName("IDs");       //    "IDs":
        writer.WriteStartArray();              //    [
        writer.WriteValue(123);                //      123,
        writer.WriteValue(456);                //      456
        writer.WriteEndArray();                //    ]
        writer.WriteEndObject();               //  }
        writer.WriteEndObject();               //}
    }
}

string jsonText = sb.ToString();

System.Diagnostics.Debug.WriteLine(jsonText);

この例を実行すると出力ウィンドウには次のように出力されます。

{
  "Team": {
    "TeamName": "公安9課",
    "Authority": "内務省",
    "Leader": {
      "Name": "草薙素子"
    },
    "IDs": [
      123,
      456
    ]
  }
}

逐一命令するのであれば文字列を直接生成しても同じように思うかもしれませんが、JSON.NETを使って書き込むことで、他の便利なJSON.NETの機能と連携できます。

たとえば、この例ではJsonTextWriterクラスのFormattingプロパティを使って改行とインデントをつけるようにしています。この行をコメントにするだけで、改行されずにすべて1行で出力されます。

他にもJsonTextWriterクラスは便利な機能を持っています。QuoteNameプロパティをFalseにセットして、プロパティ名に " をつけないようにしたり、DateFormatStringプロパティを使って、日付をJSON化するときの書式を一律で指定したり、StringEscapeHandlingプロパティを使って日本語をUnicodeエンコードしたりなどが簡単にできます。自分で文字列でJSONを生成している場合は、このようなちょっとした変更が簡単にはできません。

 

方法3:DictionaryからJSONを生成する

Dictionaryはキーと値を保存する構造であり、ほとんどのJSONと同じです。

JSON.NETはDictionaryをもとにして簡単にJSONを生成することができます。Dictionaryの項目の値をDictionaryにすることで入れ子の構造も表現できます。

VB

VB2005対応 VB2008対応 VB2010対応 VB2012対応 VB2013対応 VB2015対応 VB2017対応 VB2019対応

Dim productInfo As New Dictionary(Of String, Object)
Dim image As New Dictionary(Of String, Object)
Dim thumbnail As New Dictionary(Of String, Object)

productInfo.Add("Image", image)
image.Add("Width", 800)
image.Add("Height", 600)
image.Add("Title", "15階からの眺望")
image.Add("Thumbnail", thumbnail)
thumbnail.Add("Url", "http://www.example.com/image/481989943")
thumbnail.Add("Height", 125)
thumbnail.Add("Width", 100)
image.Add("Animated", False)
image.Add("IDs", {116, 943, 234, 38793})

Dim jsonText As String = JsonConvert.SerializeObject(productInfo, Formatting.Indented)

Debug.WriteLine(jsonText)

C#

var productInfo = new Dictionary<string, object>();
var image = new Dictionary<string, object>();
var thumbnail = new Dictionary<string, object>();

productInfo.Add("Image", image);
image.Add("Width", 800);
image.Add("Height", 600);
image.Add("Title", "15階からの眺望");
image.Add("Thumbnail", thumbnail);
thumbnail.Add("Url", "http://www.example.com/image/481989943");
thumbnail.Add("Height", 125);
thumbnail.Add("Width", 100);
image.Add("Animated", false);
image.Add("IDs", new int[] { 116, 943, 234, 38793 });

string jsonText = JsonConvert.SerializeObject(productInfo, Formatting.Indented);

System.Diagnostics.Debug.WriteLine(jsonText);

 

この例を実行すると出力ウィンドウには次のように出力されます。

{
  "Image": {
    "Width": 800,
    "Height": 600,
    "Title": "15階からの眺望",
    "Thumbnail": {
      "Url": "http://www.example.com/image/481989943",
      "Height": 125,
      "Width": 100
    },
    "Animated": false,
    "IDs": [
      116,
      943,
      234,
      38793
    ]
  }
}

 

方法4:コレクション初期化子を使ってJSONを生成する

Dictionaryを使ってJSONを生成する方法と同じようなものですが、Dictionaryの内容をコレクション初期化子を活用して記述することで、プログラムの雰囲気は全然違うものなります。

VB

VB2010対応 VB2012対応 VB2013対応 VB2015対応 VB2017対応 VB2019対応

Dim jsonText As String = JsonConvert.SerializeObject(New Dictionary(Of String, Object) From {
    {"Image", New Dictionary(Of String, Object) From {
        {"Width", 800},
        {"Height", 600},
        {"Title", "15階からの眺望"},
        {"Thumbnail", New Dictionary(Of String, Object) From {
            {"Url", "http://www.example.com/image/481989943"},
            {"Height", 125},
            {"Width", 100}}},
        {"Animated", False},
        {"IDs", {116, 943, 234, 38793}}}}},
    Formatting.Indented)

Debug.WriteLine(jsonText)

C#

string jsonText = JsonConvert.SerializeObject(new Dictionary<string, object> {
    {"Image", new Dictionary<string, object> {
        {"Width", 800},
        {"Height", 600},
        {"Title", "15階からの眺望"},
        {"Thumbnail", new Dictionary<string, object> {
            {"Url", "http://www.example.com/image/481989943"},
            {"Height", 125},
            {"Width", 100}}},
        {"Animated", false},
        {"IDs", new int[]{ 116, 943, 234, 38793}}}}},
    Formatting.Indented);

System.Diagnostics.Debug.WriteLine(jsonText);

 

この例を実行すると出力ウィンドウには他の例と同じ次の内容が出力されます。

{
  "Image": {
    "Width": 800,
    "Height": 600,
    "Title": "15階からの眺望",
    "Thumbnail": {
      "Url": "http://www.example.com/image/481989943",
      "Height": 125,
      "Width": 100
    },
    "Animated": false,
    "IDs": [
      116,
      943,
      234,
      38793
    ]
  }
}

 

方法5:匿名型を使ってJSONを生成する

これも Dictionaryを使ってJSONを生成する方法やコレクション初期化子を使う方法と同じようなものですが、こういう書き方もできるという例を紹介しておきます。

VB

VB2010対応 VB2012対応 VB2013対応 VB2015対応 VB2017対応 VB2019対応

Dim productInfo = New With {
    .Image = New With {
        .OuterWidth = 800,
        .OuterHeight = 600,
        .Title = "15階からの眺望",
        .Thumbnail = New With {
            .ImageUrl = "http://www.example.com/image/481989943",
            .Height = 125,
            .Width = 100
        },
        .Animated = False,
        .IDs = {116, 943, 234, 38793}
    }
}

Dim jsonText As String = JsonConvert.SerializeObject(productInfo, Formatting.Indented)

Debug.WriteLine(jsonText)

C#

var productInfo = new
{
    Image = new
    {
        OuterWidth = 800,
        OuterHeight = 600,
        Title = "15階からの眺望",
        Thumbnail = new
        {
            ImageUrl = "http://www.example.com/image/481989943",
            Height = 125,
            Width = 100
        },
        Animated = false,
        IDs = new int[] { 116, 943, 234, 38793 }
    }
};


string jsonText = JsonConvert.SerializeObject(productInfo, Formatting.Indented);

System.Diagnostics.Debug.WriteLine(jsonText);

 

この例を実行すると出力ウィンドウには他の例と同じ次の内容が出力されます。

{
  "Image": {
    "Width": 800,
    "Height": 600,
    "Title": "15階からの眺望",
    "Thumbnail": {
      "Url": "http://www.example.com/image/481989943",
      "Height": 125,
      "Width": 100
    },
    "Animated": false,
    "IDs": [
      116,
      943,
      234,
      38793
    ]
  }
}

 

参考

.NET での JSON のシリアル化と逆シリアル化 (マーシャリングとアンマーシャリング)-概要

https://docs.microsoft.com/ja-jp/dotnet/standard/serialization/system-text-json-overview

 

Newtonsoft. Json から system.string に移行する方法

https://docs.microsoft.com/ja-jp/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to

 

Try the new System.Text.Json APIs

https://devblogs.microsoft.com/dotnet/try-the-new-system-text-json-apis/