概要

  • WebAPIへデータをJSONで渡すにあたってApexのJSONGeneratorを調査し、試したことや思ったことを順番に記録したもの

やりたいこと

使い方

パターン1

  • key/valueをセットする方法です
  • writeNumberField()やwriteStringField()のように write~Field()メソッドが該当します

ソース

JSONGenerator g = JSON.createGenerator(false);
g.writeStartObject();
g.writeNumberField('number', 12345);
g.writeEndObject();
System.debug(g.getAsString());

結果

{"number":12345}

パターン2

  • keyとvalueを別々にセットする方法です
  • witeFieldName()してからwrite~()します

ソース

JSONGenerator g = JSON.createGenerator(false);
g.writeStartObject();
g.writeFieldName('number');
g.writeNumber(7890);
g.writeEndObject();
System.debug(g.getAsString());

結果

{"number":7890}

利用イメージをふくらませる

  • writeStartObject()で「{」を挿入
  • 必要なキーと値をセット
  • 配列の場合は writeStartArray()で「[」を挿入
  • 全部セットしたら getAsString()でテキストとして取り出してWebAPIをコール

疑問(脱線)

  • JSONGeneratorクラスにはメソッドがたくさんありますが、上記パターン1と2のメソッドが型(プリミティブ型)ごとにたくさんある感じですね
* オーバーロードしてwriteValue()とかにまとめられそうな気配は感じます
  • (writeNumber()とwriteNumberField()については別型でオーバーロードしていますが?)
* JSON表記上、クオートするかどうかとかそういう問題でしょうか
* https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_class_System_JsonGenerator.htm

オブジェクト型

  • それはそうと、オブジェクト型を取り扱えるのは良いです

まずはリスト型

ソース

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());

結果

{"numbers":[100,200]}

まるごとオブジェクト

  • ここまでの例では 「writeStartObject()」という呪文が必要でした(要は 「{」を挿入するステートですね)
  • オブジェクトの場合は、いきなり「writeObject()」が可能です。オブジェクトですから、「{」が含まれているんですね
  • サンプルは省略しますが、メンバ変数にオブジェクトのリスト(配列)も定義可能です。

ソース

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;
}

結果

{"price":1200,"name":"Hoge-0001","code":"0001"}

これだけで良いのでは

  • 上記、まるごとオブジェクトパターンがあれば、もはやこれだけでいいのでは?と思ったものでした
  • 「{」を挿入するために「writeStartObject()」を記述するということは、実装者は文字列編集を意識する必要があるということです
  • まるごとオブジェクトパターンでは、クラス定義が必要になりますが、むしろ構造が明確になり、編集機能はJSONGeneratorに任せるという、あるべき姿ではないでしょか

予約キーワード(予約語)の壁

  • 結論としては「これだけでは良くない」です
  • オブジェクトの場合、メンバ変数名がkeyとして採用される振る舞いになっています
  • WebAPI連携する場合、keyは相手システムが提示する訳ですが、要求されたkey文字列がApexで予約キーワードだったりして記述できないケースがありました
  • 下記はkeyとして「number」を要求されているのでそのようにクラス定義したものですが、numberはApex予約キーワードのため、コンパイルエラーになります
  • 無念です

ソース

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;
}

結果

Line: 11, Column: 20
Identifier name is reserved: number

facebook slideshare rubygems github qiita