雑記 |
Visual Basic 中学校 > 雑記 >
最終更新日 2020/5/23 公開日 2020/3/20
この記事が対象とする製品・バージョン
![]() |
Visual Studio 2019 | ◎ | 対象です。 |
![]() |
Visual Studio 2017 | ◎ | 対象です。 |
![]() |
Visual Studio 2015 | ◎ | 対象です。 |
![]() |
Visual Studio 2013 | ◎ | 対象ですが、サンプルの一部は動作しません。 |
![]() |
Visual Studio 2012 | ◎ | 対象ですが、サンプルの一部は動作しません。 |
![]() |
Visual Studio 2010 | ◎ | 対象ですが、サンプルの一部は動作しません。 |
![]() |
Visual Studio 2008 | ◎ | 対象ですが、サンプルの一部は動作しません。 |
![]() |
Visual Studio 2005 | ◎ | 対象ですが、サンプルの一部は動作しません。 |
![]() |
Visual Studio 2003 | × | 対象外です。 |
![]() |
Visual Studio (2002) | × | 対象外です。 |
目次
この記事では JSON.NET を扱います。System.Text.Jsonについては下記の記事で説明しています。
VB/C#でJSONを読み書きするには、JSON.NET(Newtonsoft JSON)またはSystem.Text.Jsonを使用するのが一般的です。
JSON.NETは非常に人気のあるライブラリで多機能です。あまりにもよくできていたためマイクロソフトのASP.NET CoreですらこのJSON.NETに依存する状況が発生してしまいました。これだとASP.NET CoreとJSON.NETそれぞれのバージョンが束縛されてしまいますので、マイクロソフトはJSONを扱う機能を小さなライブラリとして作成して、ASP.NET CoreをJSON.NETの依存から脱却させました。それがSystem.Text.Jsonです。このような事情ですのでどちらを使うのが良いということは特にありません。System.Text.Jsonの機能で十分であれば、それでよいですし、いろいろと複雑な解析が必要であれば高機能なJSON.NETを使うと良いでしょう。
この記事ではJSON.NETを扱います。
なお、JSONの仕様については下記の記事で説明しています。
JSON.NET(Newtonsoft JSON)は、プロジェクトの種類によってははじめから使えるようになっていますが、WindowsフォームアプリケーションなどではNuGetからインストールする必要があります。
NuGetからインストールする場合 Newtonsoft.Json で検索します。かなりメジャーなパッケージなので検索しなくてもはじめから一番上に表示されているかもしれません。
JSON.NETでは { } で囲まれたオブジェクトは JObject クラスで表現します。
オブジェクトは 名前 : 値 の0個以上のペアから構成されるのがJSONの仕様なので、JSON.NETではこのペアを JPropertyクラスで表現します。
たとえば、下記のJSONを考えて見ます。
{ } で囲まれたオブジェクトは3つあり、これらはJObjectクラスで表現します。
それぞれのオブジェクト内に名前:値のぺアがあります。名前に着目して拾ってみると、"Image", "Width", "Height", "Title", "Thumbnail", "Url", "Height", "Width", "Animated", "IDs" の10個のペアがあることがわかります。これらの10個はJPropertyクラスで表現されます。上の図では10個のうち、2つだけ赤い枠で囲ってJPropertyと書いています。
値にはいろいろな型があるので理解を深めるために、名前と値の型を一覧にして確認してみましょう。
名前 | 値の型 |
Image | オブジェクト |
Width | 数値 |
Height | 数値 |
Title | 文字列 |
Thumbnail | オブジェクト |
Url | 文字列 |
Height | 数値 |
Width | 数値 |
Animated | 論理 |
IDs | 配列 |
最後の、IDsの値は [ ] で囲まれており、配列です。JSON.NETではJArrayクラスで配列を表現します。
JObject, JProperty, JArrayの各クラスは JToken を基底クラスとします。
JSON.NETでJSONを読み込んで処理をするには大きく分けて3つのアプローチがあります。
このJSONを例に簡単にどのようなアプローチかを紹介します。
JSONの定義があらかじめわかっている場合はVB/C#で同じ構造のクラスを作成しておくと、JSON.NETの機能でそのクラスに値を割り当ててくれます。クラスを定義するのは少し面倒かもしれませんが、読み込んだ後の処理では完全にVB/C#の世界になるのでJSONを意識することがありません。つまり、上述で説明したようなJObjectやJPropertyなどのことを知らなくてもよいということです。(とはいえ、いろいろ作っているとどうしても簡単にマッピングしただけでは済まない事態も発生するので、やはり、JSON.NETがどういう考え方なのか上述の説明は理解しておいたほうが良いです。)
まず、上述のJSONをVB/C#のクラスで表現して次のような構造を作成します。プロパティ名は完全にJSONと一致させるのが楽ですが、クラス名は自由です。
Visual Studioの編集メニューから[形式を選択して貼り付け] - [JSONをクラスとして貼り付ける]を選択すると、JSONをVB/C#のクラスに変換して貼り付けてくれるのでこの手のクラスを定義する際に便利です。ただし、思ったように変換してくれない部分を手で修正する必要がある場合もよくあります。
VB
Public
Class ProductInfo Public Property Image As ProductImage End Class Public Class ProductImage Public Property Width As Integer Public Property Height As Integer Public Property Title As String Public Property Thumbnail As ProductImageThumbnail Public Property Animated As Boolean Public Property IDs As Integer() End Class Public Class ProductImageThumbnail Public Property Url As String Public Property Height As Integer Public Property Width As Integer End Class |
C#
public
class ProductInfo { public ProductImage Image { get; set; } } public class ProductImage { public int Width { get; set; } public int Height { get; set; } public string Title { get; set; } public ProductImageThumbnail Thumbnail { get; set; } public bool Animated { get; set; } public int[] IDs { get; set; } } public class ProductImageThumbnail { public string Url { get; set; } public int Height { get; set; } public int Width { get; set; } } |
後は簡単で、次のようにして、このクラスとJSONをマッピングできます。
マッピングするにはJsonConvertクラスのDesrializeObjectメソッドを使用し、型パラメーターにマッピング先の型を指定します。
VB
Private
Function LoadJsonText() As String Dim json As New System.Text.StringBuilder json.AppendLine("{") json.AppendLine(" ""Image"": ") json.AppendLine(" {") json.AppendLine(" ""Width"": 800,") json.AppendLine(" ""Height"": 600,") json.AppendLine(" ""Title"": ""View from 15th Floor"",") json.AppendLine(" ""Thumbnail"": ") json.AppendLine(" {") json.AppendLine(" ""Url"": ""http://www.example.com/image/481989943"",") json.AppendLine(" ""Height"": 125,") json.AppendLine(" ""Width"": 100") json.AppendLine(" },") json.AppendLine(" ""Animated"" : false,") json.AppendLine(" ""IDs"": [116, 943, 234, 38793]") json.AppendLine(" }") json.AppendLine("}") Return json.ToString End Function Private Sub TestJson() Dim jsonText As String = Me.LoadJsonText Dim root As ProductInfo = Newtonsoft.Json.JsonConvert.DeserializeObject(Of ProductInfo)(jsonText) End Sub |
C#
private
string LoadJsonText() { StringBuilder json = new StringBuilder(); json.AppendLine("{"); json.AppendLine(" \"Image\": "); json.AppendLine(" {"); json.AppendLine(" \"Width\": 800,"); json.AppendLine(" \"Height\": 600,"); json.AppendLine(" \"Title\": \"View from 15th Floor\","); json.AppendLine(" \"Thumbnail\": "); json.AppendLine(" {"); json.AppendLine(" \"Url\": \"http://www.example.com/image/481989943\","); json.AppendLine(" \"Height\": 125,"); json.AppendLine(" \"Width\": 100"); json.AppendLine(" },"); json.AppendLine(" \"Animated\" : false,"); json.AppendLine(" \"IDs\": [116, 943, 234, 38793]"); json.AppendLine(" }"); json.AppendLine("}"); return json.ToString(); } private void TestJson() { string jsonText = this.LoadJsonText(); ProductInfo root = Newtonsoft.Json.JsonConvert.DeserializeObject<ProductInfo>(jsonText); } |
この例では、マッピングした後、何もしていませんが、root変数の内容を見ると、ちゃんと値が入っているのわかります。
ここまでできれば後はプログラムで自由に利用できますね。
JSONの定義がわかっているならば、クラスを定義しなくても値を読み取ることが可能です。
このアプローチはさらにいくつかの手段にわかれます。代表的なのは動的ランタイムを使用する方法です。
たとえば、次のようにしてUrlプロパティの値を読み込むことができます。
このプログラム中に登場する LoadJsonTextメソッドは上述のプログラム例に登場するものと同じなので割愛しています。
VBの例を実行するにはOption StrictがOffである必要があります。
VB
Private Sub
TestJson() Dim jsonText As String = Me.LoadJsonText Dim root As Newtonsoft.Json.Linq.JObject = DirectCast(Newtonsoft.Json.JsonConvert.DeserializeObject(jsonText), Newtonsoft.Json.Linq.JObject) Dim url As String = root("Image")("Thumbnail")("Url").ToString End Sub |
C#の場合、スマートに書けます。
C#
private
void TestJson() { string jsonText = this.LoadJsonText(); dynamic root = Newtonsoft.Json.JsonConvert.DeserializeObject(jsonText); string url = root.Image.Thumbnail.Url; } |
VBでも少し細工をすればC#のように自前のプロパティであるかのごとくアクセスすることが可能です。
それにはここで解説されているDynamicAdapterクラスを定義します。
このクラスが存在すれば次のように記述できます。この例は Option Strict が Offである必要があります。
VB
Private Sub
TestJson() Dim jsonText As String = Me.LoadJsonText Dim root As Object = New DynamicAdapter(Newtonsoft.Json.JsonConvert.DeserializeObject(jsonText)) Dim url As String = root.Image.Thumbnail.Url End Sub |
なお、JSON内の位置を指定して値を取り出すにはJSONパスを利用することもできます。これを使うとUrlは次のように取り出せます。
下記のプログラムを実行するには冒頭にVBの場合、 Imports Newtonsoft.Json.Linq が、C#の場合、 using Newtonsoft.Json.Linq; が必要です。
JSONパスについてはこちらに説明があります。
https://goessner.net/articles/JsonPath/
VB
Private Sub
TestJson() Dim jsonText As String = Me.LoadJsonText Dim root As JToken = DirectCast(Newtonsoft.Json.JsonConvert.DeserializeObject(jsonText), JToken) Dim target As String = root.SelectToken("$.Image.Thumbnail.Url").ToString End Sub |
C#
private
void TestJson() { string jsonText = this.LoadJsonText(); JToken root = Newtonsoft.Json.JsonConvert.DeserializeObject(jsonText) as JToken; string url = root.SelectToken("$.Image.Thumbnail.Url").ToString(); } |
JSONの定義があらかじめわかっていない場合、For Each(foreach)の列挙や、条件分岐、再帰などを使って構造を解析したり、値を検索、取得していくことになります。
このときに登場するのが前に説明したJObjectやJPropertyなどのクラス群です。これらのクラスの多くはNewtonsoft.Json.Linq名前空間で定義されています。
下記のプログラムを実行するには冒頭にVBの場合、 Imports Newtonsoft.Json.Linq が、C#の場合、 using Newtonsoft.Json.Linq; が必要です。
このプログラムでは読み込んでJSONの要素がオブジェクトなのか配列なのかその他なのかで分岐、オブジェクトの場合はプロパティの列挙、配列の場合は値の列挙、値の場合は値を表示するという処理を行っています。
上述のJSONでのみテストした簡易的な実装であり、対応できないケースもあるのではないかと思いますが、構造がわからないJSONに対するアプローチを示すのには十分だと思います。
VB
Private
Sub TestJson() Dim jsonText As String = Me.LoadJsonText Dim root As JToken = DirectCast(Newtonsoft.Json.JsonConvert.DeserializeObject(jsonText), JToken) Parse(root) End Sub Private Sub Parse(source As JToken) If (source.Type = JTokenType.Object) Then Debug.WriteLine("(オブジェクト)") ParseObject(DirectCast(source, JObject)) ElseIf (source.Type = JTokenType.Array) Then Debug.WriteLine("(配列)") ParseArray(DirectCast(source, JArray)) Else Debug.WriteLine(source) End If End Sub Private Sub ParseObject(source As JObject) For Each prop In source.Properties Debug.Write($"{prop.Name} : ") Parse(prop.Value) Next End Sub Private Sub ParseArray(source As JArray) For Each value In source Parse(value) Next End Sub |
C#
private
void TestJson() { string jsonText = this.LoadJsonText(); JToken root = Newtonsoft.Json.JsonConvert.DeserializeObject(jsonText) as JToken; Parse(root); } private void Parse(JToken source) { if (source.Type == JTokenType.Object) { System.Diagnostics.Debug.WriteLine("(オブジェクト)"); ParseObject(source as JObject); } else if (source.Type == JTokenType.Array) { System.Diagnostics.Debug.WriteLine("(配列)"); ParseArray(source as JArray); } else { System.Diagnostics.Debug.WriteLine(source); } } private void ParseObject(JObject source) { foreach(var prop in source.Properties()) { System.Diagnostics.Debug.Write($"{prop.Name} : "); Parse(prop.Value); } } private void ParseArray(JArray source) { foreach(var value in source) { Parse(value); } } |
このプログラムを実行すると出力ウィンドウには次のように出力されます。
(オブジェクト) Image : (オブジェクト) Width : 800 Height : 600 Title : View from 15th Floor Thumbnail : (オブジェクト) Url : http://www.example.com/image/481989943 Height : 125 Width : 100 Animated : False IDs : (配列) 116 943 234 38793 |
JObjectやJPropertyなどの理解を深める練習用のサンプルを紹介しておきます。
Private Sub TestJson() Dim jsonText As String = Me.LoadJsonText '一番外側はオブジェクトなので、JObjectが返されます。 Dim root As JObject = Newtonsoft.Json.JsonConvert.DeserializeObject(jsonText) Debug.WriteLine($"一番外側のオブジェクトのプロパティ数:{root.Properties.Count}") '唯一のプロパティを取得 Dim prop1 As JProperty = root.Properties(0) Debug.WriteLine($"プロパティ名:{prop1.Name}") Debug.WriteLine($"値の型:{prop1.Value.Type.ToString}") 'Imageプロパティの値はオブジェクトであることがわかっているので取得します。 Dim middle As JObject = prop1.Value Debug.WriteLine($"真ん中のオブジェクトのプロパティ数:{middle.Properties.Count}") 'プロパティを列挙します。 For Each prop In middle.Properties Debug.WriteLine($"プロパティ名:{prop.Name}") Debug.WriteLine($"プロパティの値:{prop.Value}") Debug.WriteLine($"値の型:{prop.Value.Type.ToString}") Next '最後のプロパティは配列であることがわかっているのでJArrayで取得します。 Dim lastProp As JArray = middle.Properties.Last.Value '配列の値を列挙します。 For Each value In lastProp Debug.WriteLine($"配列の値:{value}") Next End Sub |
参考
.NET での JSON のシリアル化と逆シリアル化 (マーシャリングとアンマーシャリング)-概要
https://docs.microsoft.com/ja-jp/dotnet/standard/serialization/system-text-json-overview
Newtonsoft. Json から system.string に移行する方法
Try the new System.Text.Json APIs
https://devblogs.microsoft.com/dotnet/try-the-new-system-text-json-apis/