概要
- WebAPIへデータをJSONで渡すにあたってApexのJSONGeneratorを調査し、試したことや思ったことを順番に記録しました
- ありていにいえば、やってみたメモ記事です
やりたいこと
- ApexでJSON文字列を生成します
- WebAPIへJSON形式で情報を渡すことが目的です
- 最終的に文字列なので、頑張って編集することもできますが、ApexではJSONサポート機能が含まれているのでこれ利用し、スマートに実装したいと思っています
- Apex開発者ガイド JSONサポート
使い方
- JSON生成サンプルを参照すると、大きく2パターンあるようです
- Apex開発者ガイド JSON ジェネレータ
使い方パターン1
- key/valueをセットする方法です
-
writeNumberField()
やwriteStringField()
のように、write~Field()
メソッドが該当します
sample1
JSONGenerator g = JSON.createGenerator(false);
g.writeStartObject();
g.writeNumberField('number', 12345);
g.writeEndObject();
System.debug(g.getAsString());
sample1-result
{"number":12345}
使い方パターン2
- keyとvalueを別々にセットする方法です
-
witeFieldName()
してからwrite~()
します
sample2
JSONGenerator g = JSON.createGenerator(false);
g.writeStartObject();
g.writeFieldName('number');
g.writeNumber(7890);
g.writeEndObject();
System.debug(g.getAsString());
sample2-result
{"number":7890}
利用イメージまとめ
-
writeStartObject()
で{
を挿入 - 必要なキーと値をセット
- 配列の場合は
writeStartArray()
で[
を挿入 - 全部セットしたら
getAsString()
でテキストとして取り出してWebAPIをコール
疑問(脱線)
- JSONGeneratorクラスにはメソッドがたくさんありますが、上記パターン1と2のメソッドが型(プリミティブ型)ごとにたくさんある感じですね
- オーバーロードして
writeValue()
とかにまとめられそうな気配は感じますが、String、Number、Booleanなど型ごとにメソッド名が分けられています - (
writeNumber()
とwriteNumberField()
については別型でオーバーロードしていますが) - JSON表記上、クオートするかどうかとか、そういう観点で分けているのでしょうか。(ただの推測です)
- 参考:Apex開発者ガイド JSONGeneratorクラス
オブジェクト型
- プリミティブ型の他に、オブジェクト型を取り扱えます
- これは良いです
リスト型のサンプル
sample-list
List<Integer> numbers = new List<Integer>();
numbers.add(100);
numbers.add(200);
JSONGenerator g = JSON.createGenerator(false);
g.writeStartObject();
g.writeObjectField('numbers', numbers);
g.writeEndObject();
System.debug(g.getAsString());
sample-list-result
{"numbers":[100,200]}
オブジェクト型のサンプル
- ここまでの例では
writeStartObject()
という呪文が必要でした(要は{
を挿入するステートですね) - オブジェクトの場合は、いきなり
writeObject()
が可能です。オブジェクトですから、{
が含まれているんですね - WebAPIで渡すパラメータなんてたいてい一番外側がオブジェクトな気がするので、JSON全体を大きなオブジェクトにしてしまえば良さそう、つまり、まるごとオブジェクトJSONパターンです
- サンプルは省略しますが、メンバ変数にオブジェクトのリスト型(配列)も定義可能です
sample-object
Hoge h = new Hoge();
h.code = '0001';
h.name = 'Hoge-0001';
h.price = 1200;
JSONGenerator g = JSON.createGenerator(false);
g.writeObject(h);
System.debug(g.getAsString());
public class Hoge {
public String code;
public String name;
public Decimal price;
}
sample-object-result
{"price":1200,"name":"Hoge-0001","code":"0001"}
これだけで良いのでは
- 上記、まるごとオブジェクトパターンがあれば、もはやこれだけでいいのでは?と、さっきまで思っていました
-
{
を挿入するためにwriteStartObject()
を記述するということは、実装者は文字列編集を意識する必要があるということです
- まるごとオブジェクトパターンでは、クラス定義が必要になりますが、むしろ構造が明確になり、編集機能はJSONGeneratorに任せるという、あるべき実装の姿ではないでしょうか
予約キーワード(予約語)の壁
- 結論としては「これだけでは良くない」です
- 予約語をkeyして指定したい場合、そのまま記述できません
- オブジェクトの場合、メンバ変数名がkeyとして採用される振る舞いになっています
- WebAPI連携する場合、keyは相手システムが提示する訳ですが、要求されたkey文字列がApexで予約キーワードだったりして記述できないケースがありました
- 下記サンプルはkeyとして
number
を要求されていたため、そのようにクラス定義したものですが、numberはApex予約キーワードのため、コンパイルエラーになります - すごくキレイに記述できそうなのにできない。おしい。残念です。
sample-error
Hoge h = new Hoge();
h.number = '0001';
h.name = 'Hoge-0001';
h.price = 1200;
JSONGenerator g = JSON.createGenerator(false);
g.writeObject(h);
System.debug(g.getAsString());
public class Hoge {
public String number;
public String name;
public Decimal price;
}
sample-error-result
Line: 11, Column: 20
Identifier name is reserved: number