tyoshikawa1106のブログ

- Force.com Developer Blog -

SFDC:Object型変数をApexで扱う方法について

Object型変数をApexで用意する方法についてです。SalesforceにはAccountやContactなどのsObject型が用意されていますが、通常のObject型も利用することができます。


Object型変数はJSON文字列を変換して用意することができます。ひとつの方法ですがJavaScript側で次のようにJSON文字列を用意します。

JavaScript
// JSでオブジェクトを用意
var accountObj = {
  Id: '',
  Name: 'Sample',
  AccountNumber: 'A-001'
};
// JSON文字列に変換
var accountJSON = JSON.stringify(accountObj);

これをRemoteActionをつかってApexに渡すことができます。(実際にRemoteActionで渡すときはオブジェクトのまま渡して、AccountなどのsObject型変数で受け取ることができるのでそちらの方法が便利です。)


Apex側ではJSON文字列を次の方法でMap型変数に変換できます。

Map<String, Object> jsonMap = (Map<String, Object>) JSON.deserializeUntyped(accountJSON);


ここまでくれば、キーとなる値を指定するだけで値を取得することが可能です。

public Account getAccountByJson(Map<String, Object> jsonMap) {
    Account account = new Account();
    if (jsonMap.containsKey('Id') && String.isNotEmpty((String)jsonMap.get('Id'))) {
        account.Id = (String)jsonMap.get('Id');
    }
    if (jsonMap.containsKey('Name') && String.isNotEmpty((String)jsonMap.get('Name'))) {
        account.Name = (String)jsonMap.get('Name');
    }
    if (jsonMap.containsKey('AccountNumber') && String.isNotEmpty((String)jsonMap.get('AccountNumber'))) {
        account.AccountNumber = (String)jsonMap.get('AccountNumber');
    }
    return account;
}


ここで意識しておくと良いのがテストクラスの実装についてです。テストクラスではApex側でJSON文字列を用意して対応する必要があります。Apex内でJSON文字列を用意したい場合は、JSONGeneratorが便利です。

// JSON文字列の生成
JSONGenerator gen = JSON.createGenerator(true);
gen.writeStartObject();
gen.writeIdField('Id', account.Id);
gen.writeStringField('Name', account.Name);
gen.writeStringField('AccountNumber', account.AccountNumber);
gen.writeEndObject();
// これでJSON文字列を作成
String accountJSON = gen.getAsString();
// あとはMapに変換するだけです
Map<String, Object> jsonMap = (Map<String, Object>) JSON.deserializeUntyped(accountJSON);


これでテストクラスの実装も問題なく行えると思います。

リスト形式のJSON文字列について

1件のデータをJSON文字列として渡した場合、Map<String, Object>型として扱うことになっていました。ですが、複数件分のデータをJSON文字列として渡した場合はList<Object>型変数として扱うことになります。

List<Object> jsonContactList = (List<Object>) JSON.deserializeUntyped(contactJSON);


この場合は次の手順で値を取得できます。

public List<Contact> getContactsByJson(Id accountId, List<Object> jsonContactList) {

    List<Contact> contacts = new List<Contact>();
    for (Object item : jsonContactList) {
        Map<String, Object> itemMap = (Map<String, Object>)item;
        Boolean isChanged = false;

        Contact contact = new Contact();
        contact.AccountId = accountId;

        if (itemMap.containsKey('IsChanged')) {
            isChanged = (Boolean)itemMap.get('IsChanged');
        }
        if (itemMap.containsKey('Id') && String.isNotEmpty((String)itemMap.get('Id'))) {
            contact.Id = (Id)itemMap.get('Id');
        }
        if (itemMap.containsKey('LastName') && String.isNotEmpty((String)itemMap.get('LastName'))) {
            contact.LastName = (String)itemMap.get('LastName');
        }
        if (itemMap.containsKey('FirstName') && String.isNotEmpty((String)itemMap.get('FirstName'))) {
            contact.FirstName = (String)itemMap.get('FirstName');
        }
        if (itemMap.containsKey('LeadSource') && String.isNotEmpty((String)itemMap.get('LeadSource'))) {
            contact.LeadSource = (String)itemMap.get('LeadSource');
        }
        if (itemMap.containsKey('Description') && String.isNotEmpty((String)itemMap.get('Description'))) {
            contact.Description = (String)itemMap.get('Description');
        }
        // IDがなし(新規) or 値変更(更新)の場合、登録対象としてリストに追加
        if (String.isEmpty(contact.Id) || isChanged) {
            contacts.add(contact);
        }
    }

    return contacts;
}


テストクラスで用意する場合は次のようになります。

// JSON文字列の生成
JSONGenerator gen = JSON.createGenerator(true);
gen.writeStartObject();
gen.writeBooleanField('IsChanged', true);
gen.writeIdField('Id', contact.Id);
gen.writeStringField('LastName', contact.LastName);
gen.writeStringField('FirstName', contact.FirstName);
gen.writeStringField('LeadSource', contact.LeadSource);
gen.writeStringField('Description', contact.Description);
gen.writeEndObject();

// [ ] で囲むとList<Object>に変換されます。
String contactJSON  = '[' + gen.getAsString() + ']';
List<Object> jsonContactList = (List<Object>) JSON.deserializeUntyped(contactJSON);

キーの無いJSON文字列の場合

オブジェクトで渡した場合は上記の通りですが、次のように配列で渡す場合もあると思います。

["xxxId", "xxxId"]


こんな感じのJSON文字列をApex側で用意する場合は次の書き方で大丈夫です。

String deleteContactIdJSON = '[' + '\"' + contact.Id + '\"' + ']';
List<Object> jsonDelContactIdList = (List<Object>) JSON.deserializeUntyped(deleteContactIdJSON);


以上がObject型変数をApexで扱う方法についてです。RemoteActionをつかってJSとApexで値のやり取りを行う場合はJSON文字列よりクラスで渡す方がシンプルにできると思いますが、こういった方法があることも覚えておくと良いんじゃないかと思います。