大阪市中央区 システムソフトウェア開発会社

営業時間:平日09:15〜18:15
MENU

Wikidataで遊んでみた その2 – C#におけるJSONデータのデシリアライズ

著者:北本 敦
公開日:2020/07/28
最終更新日:2020/07/28
カテゴリー:技術情報

北本です。

前回、C#からWikidataにSPARQL文のクエリを送って、ピアノソナタを書いている作曲家を生年月日順に並べたデータをXML形式やJSON形式で取得しました。しかし、そのままではC#でうまく扱うことが出来ませんので、データをC#のオブジェクトに変換する必要があります。今回は、JSON形式のデータからの変換を取り上げてみたいと思います。

C#でJSONをシリアライズ/デシリアライズする方法はいくつかありますが、ここではSystem.Text.Jsonを使ってみることにします。

System.Text.Jsonは、.NET Core 3.0では共有フレームワークに組み込まれていますが、それ以外の.NETフレームワークで利用する場合は、NuGetパッケージをインストールする必要があります。

インストールの仕方は以下の通りです。

ソリューションエクスプローラーで「参照」を右クリックし「NuGetパッケージの管理」を選択。

NuGetパッケージマネージャーで、「Text.Json」などと検索し、System.Text.Jsonを選んでインストール。

すると、System.Text.Json名前空間が使えるようになります。

JSONデータをオブジェクトに変換するのには、JsonSerializer.Deserializeメソッドを使います。staticなのでインスタンス化の必要はありません。

今回は、引数が(ReadOnlySpan<byte>, Type, JsonSerializerOptions)のものを使います。第1引数にはWikidataから取得したバイト列を渡します。なお、第1引数がStringのものもあるので、文字列に変換後のデータを使うこともできます。第2引数には変換先のオブジェクトの型を渡しますが、これについては後述します。第3引数ではオプションを設定しますが、今回は特に設定の必要がないので何も書かずデフォルトのnullを使います。

第2引数についてですが、JSONのフォーマットに合ったクラスを用意してやる必要があります。入れ子になったクラスを手動で用意しようとすると大変ですが、Visual Studioの機能で一瞬で生成することが可能です。

変換したいJSONのフォーマットで書かれた文字列をコピーし、「編集」>「形式を選択して貼り付け」>「JSONをクラスとして貼り付ける」を実行します。今回コピーするのは、前回のコードでコンソールに出力されたものでよいです。

すると、そのJSONのフォーマットに合うクラスが自動で生成されます。
今回の場合、以下のようなクラスが出来上がります。

  public class Rootobject
  {
    public Head head { get; set; }
    public Results results { get; set; }
  }

  public class Head
  {
    public string[] vars { get; set; }
  }

  public class Results
  {
    public Binding[] bindings { get; set; }
  }

  public class Binding
  {
    public Composerlabel composerLabel { get; set; }
    public Dateofbirth dateOfBirth { get; set; }
  }

  public class Composerlabel
  {
    public string xmllang { get; set; }
    public string type { get; set; }
    public string value { get; set; }
  }

  public class Dateofbirth
  {
    public string type { get; set; }
    public object value { get; set; }
    public string datatype { get; set; }
  }

では、前回のコードのMainメソッドを書き換えてみましょう。

    static void Main(string[] args)
    {
      string url = "https://query.wikidata.org/sparql";
      byte[] data;
      using (System.Net.WebClient wc = new System.Net.WebClient())
      {
        System.Collections.Specialized.NameValueCollection ps = new System.Collections.Specialized.NameValueCollection();
        ps.Add(
          "query",
          @"
            SELECT DISTINCT ?composerLabel ?dateOfBirth
            WHERE
            {
              ?composer wdt:P106 wd:Q36834;
                wdt:P569 ?dateOfBirth.
              ?work wdt:P86 ?composer;
                wdt:P31 wd:Q1546995.  
              SERVICE wikibase:label { bd:serviceParam wikibase:language ""ja, en"". }
            }
            ORDER BY ?dateOfBirth
            LIMIT 100
          ");
        ps.Add("format", "json");
        wc.Headers.Add("User-Agent: Other");
        data = wc.UploadValues(url, ps);
      }
      Rootobject rootobject = (Rootobject)JsonSerializer.Deserialize(data, typeof(Rootobject));

      foreach(Binding binding in rootobject.results.bindings)
      {
        Console.WriteLine(string.Format("{0} ({1})", binding.composerLabel.value, binding.dateOfBirth.value));
      }
    }
  }

実行すると以下のように出力されます(2020年7月28日に実行したものです)。

作者不明 (t2009759595)
フランツ・ヨーゼフ・ハイドン (1732-03-31T00:00:00Z)
ヨハン・クリストフ・フリードリヒ・バッハ (1732-06-21T00:00:00Z)
ヨハン・クリストフ・フリードリヒ・バッハ (1732-06-23T00:00:00Z)
ムツィオ・クレメンティ (1752-01-23T00:00:00Z)
ヴォルフガング・アマデウス・モーツァルト (1756-01-27T00:00:00Z)
ヤン・ラディスラフ・ドゥシーク (1760-02-12T00:00:00Z)
ルートヴィヒ・ヴァン・ベートーヴェン (1770-12-16T00:00:00Z)
フランツ・シューベルト (1797-01-31T00:00:00Z)
ミハイル・グリンカ (1804-06-01T00:00:00Z)

/// 中略 ///

ベン・ジョンストン (1926-03-15T00:00:00Z)
ジャン・バラケ (1928-01-17T00:00:00Z)
Carl Vine (1954-10-08T00:00:00Z)

前回は特に突っ込んでいませんでしたが、「作者不明」が取得されているのが気になります。どうやら、モーツァルトやベートーヴェンの贋作のピアノソナタの項目があり、それらの作曲者として「作者不明」が取得されたようです。

また、「ヨハン・クリストフ・フリードリヒ・バッハ」が2つ取得されていますが、生年月日が2通り登録されているためそうなってしまいました。

あと、「ミハイル・グリンカ」が取得されていて、グリンカがピアノソナタなんて書いていたっけと思いましたが、これはどうやら彼のヴィオラソナタに属性として「ピアノソナタ」が登録されていたのが原因のようです。

思い通りのデータを綺麗に取ろうとすると更なる工夫が必要ですが、不特定多数の人が編集しているWikidataであるので、なかなか難しいかもしれません。

    上に戻る