Delphi: TJsonSerializerを使って、Jsonを解読する方法

Delphi活用

DelphiでJSONの第2階層以降や配列化されたデータを取得する方法を紹介します。

TJsonSerializer

ここではTJsonSerializerの利用方法を紹介します。
使用例を示した方がわかりやすいと思いますので、無料で為替レート情報を提供しているGMOコインさんの為替レート取得APIで得られるJsonからデータを取リ出すプログラムを示します。

GMOコインの為替レート取得API

こちらの紹介記事で存在を知りました。GMOコインは口座開設をしなくても無料でAPIが利用出来て、APIキーが必要ないPublic APIとなっているとのことです。以前紹介したことのあるExchangeRatesのAPIは無料で利用できるAPIはHTTP接続のみでHTTPS接続は有料となるため、Adroidアプリでの利用を念頭にGMOコインの為替レート取得APIを試してみました。

APIキーが必要ないため、URLを入れるだけで簡単にJsonを確認できます。

最新レート取得API(ticker)のURLはhttps://forex-api.coin.z.com/public/v1/tickerです。Jsonを見やすくするとこんな感じです。

Jsonの構造

このようにJsonを見やすくすると、あとでレコードを作成する際に便利です

{
"status": "0",
"data": [
{
"symbol":"USD_JPY",
"ask":"149.355",
"bid":"149.263",
"timestamp":"2025-03-22T07:18:21.935993Z",
"status":"CLOSE"
}
{
"symbol":"EUR_JPY",
"ask":"161.558",
"bid":"161.445",
....
....
}
....
]
"responsetime":"2025-03-22T07:18:22.012Z"
}

今回は、USD_JPY(ドル円)のbid(売値)の値を取得することを目的にします。

Jsonの取得から文字列に変換するところまで

TJsonSerializerでは文字列にしたJsonが必要になります

var
  HTTPResponse: IHTTPResponse;
  JSon : TStringStream;
  JsonText: String;
  HTTPClient: TNetHTTPClient;
  Request : TNetHTTPRequest;
  ....
begin
  Json:= TStringStream.Create('', TEncoding.UTF8);
  Request := TNetHTTPRequest.Create(nil);
  HTTPClient:= TNetHTTPClient.Create(Request);
  try
    HTTPClient.ContentType := 'application/json';
    HTTPClient.Accept      := 'application/json';
    HTTPClient.ConnectionTimeout := 20000;
    HTTPClient.ResponseTimeout   := 20000;
    HTTPResponse:= HTTPClient.Get('https://forex-api.coin.z.com/public/v1/ticker',Json);
    JsonText:= HTTPResponse.ContentAsString(TEncoding.UTF8);
    ....

TJsonSerializerで使うレコードの準備

TJsonSerializerではJsonの要素をレコードにして扱います

type
  TData = record // dataの要素
    symbol:String;
    ask: Double;
    bid: Double;
    timestamp: String;
    status: String;
  end;

  TRoot = record // JSON 全体
    status: integer;
    data: TArray<TData>;       // TData の配列
  end;
var
....

USD_JPY(ドル円)のbid(売値)の値を取得

TJsonSerializerでJsonの要素を取り出す用例を示します

var
  ....
  JsonSerial : TJsonSerializer;
  Root : TRoot;
  LYPD : Double;
begin
  ....
    JsonSerial := TJsonSerializer.Create;
    try
      Root := JsonSerial.Deserialize<TRoot>(JsonText);
      LYPD :=  Root.data[0].bid;    //USD_JPY(ドル円)のbid(売値)の値
    finally
      JsonSerial.Free;
    end;
    ....

TJsonSerializerを使うとこんな簡単でいいのっていうぐらい、直感的ですごくわかりやすくなりますね。

関数全体

function TFormMain.GetYPD: Double;
type
  TData = record // dataの要素
    symbol:String;
    ask: Double;
    bid: Double;
    timestamp: String;
    status: String;
  end;

  TRoot = record // JSON 全体
    status: integer;
    data: TArray<TData>;       // TData の配列
  end;

var
  HTTPResponse: IHTTPResponse;
  JSon        : TStringStream;
  JsonText: String;
  HTTPClient: TNetHTTPClient;
  Request : TNetHTTPRequest;
  JsonSerial : TJsonSerializer;
  Root : TRoot;
  LYPD : Double;
begin
  Json:= TStringStream.Create('', TEncoding.UTF8);
  Request := TNetHTTPRequest.Create(nil);
  HTTPClient:= TNetHTTPClient.Create(Request);
  try
    HTTPClient.ContentType := 'application/json';
    HTTPClient.Accept      := 'application/json';
    HTTPClient.ConnectionTimeout := 20000;
    HTTPClient.ResponseTimeout   := 20000;
    HTTPResponse:= HTTPClient.Get('https://forex-api.coin.z.com/public/v1/ticker',Json);
    JsonText:= HTTPResponse.ContentAsString(TEncoding.UTF8);

    JsonSerial := TJsonSerializer.Create;
    try
      Root := JsonSerial.Deserialize<TRoot>(JsonText);
      LYPD :=  Root.data[0].bid;
    finally
      JsonSerial.Free;
    end;

  except
    on E:Exception do
    begin
      LYPD :=  0;

    end;

  end;
  Result:= LYPD;
  HTTPClient.Free;
  Request.Free;
  Json.Free;
end;

uses

System.Net.HttpClient, System.Net.HttpClientComponent,
System.JSON.Serializers

漏れがあったらごめんなさい🙇

Delphi12(FMX)で作成し、Windows11とAndroid9,12で動作を確認。

Android苦労話

VCLのRestコンポーネントを使って取得したExchangeRatesのAPIのJsonを解析するWindowsアプリをAndroidでも使えるようにFMXに移植しようとしたことが、今回の記事のネタとなったきっかけでした。

Androidで発生したエラーと原因

Windowsで動作確認をする限り、VCLからFMXへの移植は完璧に思えました。ところが、Androidで実行するとJsonを取得する過程で以下の4つのエラーが発生しました。

プロジェクト ***.apk は例外クラス EJNIException (メッセージ 'java.io.IOException: Cleartext HTTP traffic to api.exchangeratesapi.io not permitted')を送出しました。

プロジェクト ***.apk は例外クラス ENetHTTPCertificateException (メッセージ 'java.io.IOException: Cleartext HTTP traffic to api.exchangeratesapi.io not permitted')を送出しました。

プロジェクト ***.apk は例外クラス ENetHTTPCertificateException (メッセージ 'java.io.IOException: Cleartext HTTP traffic to api.exchangeratesapi.io not permitted')を送出しました。

プロジェクト ***.apk は例外クラス ERESTException (メッセージ 'REST 要求が失敗しました: java.io.IOException: Cleartext HTTP traffic to api.exchangeratesapi.io not permitted')を送出しました。

結論としては、Androidのパーミッションの問題だとわかったのですが、最初はコード内のエラーの発生源を突き止めようとして莫大な時間を浪費してしまいました。上記のコードでRestコンポーネントではなく、TNetHTTPClientなどを直接Createして使っているのは、原因追及の過程でRestコンポーネントに疑いを持ってしまったせいです。最終的には上のエラーメッセージの「Cleartext HTTP traffic to — not permitted」の部分に着目して検索したところ、こちらのページからこちらのページにたどり着き、問題点が明らかになりました。

Android(9以降?)ではHTTP接続のAPIは使えない?

らしいので、結局ExchangeRatesの使用はあきらめてHTTPS接続が可能なAPIを探すことにしました。

AndroidManifest.xmlに対する加筆

も必要みたいです。先ほど紹介したこちらのページのOption2とOption3の部分が参考になります。Option2のnetwork_security_config.xmlは
\dprojのあるフォルダ\res\xml\ に作成して念のために「配置マネージャ」に登録しました。リモートパスはres\xml\としました。
また、network_security_config.xml内のapi.example.com(to be adjusted)の部分は、forex-api.coin.z.comとしました。

GMOコインさんの為替レート取得APIのメリット

無料でHTTPS接続が可能な上に、APIキーがいらなというのはいいですね。ExchangeRatesの時はユーザーさんに自分でAPIキーを取得してもらい、APIキーをiniファイルに保存するようにしていたのですが、これらが不要になるメリットは大きいです。GMOコインさん、ありがとうございます!いつか暗号資産やFXに手を出すときがきたら利用させてもらいます🙏

便利すぎるTJsonSerializer

ExchangeRatesが使えなくなったので、GMOコイン用のJson解析コードを組むことが必要になったのですが、この記事を見つけたおかげでTJsonSerializerを知ることができました。Jsonを意識することなく、Delphiで使い慣れたオブジェクトのように扱えるのが本当に便利です。ExchangeRatesが使えないことがわかったときはショックでしたが、そのせいでGMOコインのAPIとTJsonSerializerに出会えたのは怪我の功名でした。

Android版「楽天お知らせメールを家計簿スプレッドシートに貼り付けるためのアプリ」

謎のエラーが発生したときと、ExchangeRatesが使えないとわかったときは、米国株配当金の入金のお知らせメールの解析機能の実装はあきらめるしかないと思ったのですが、結果的にいままで以上に使い勝手のいいアプリになりそうです。5月中には公開したいですね。

タイトルとURLをコピーしました