Salesforceから外部APIを叩く(JSONの解析)



Apexクラスから[Random User Generator](https://randomuser.me/)のAPI(以下、RandomUserAPI)を叩き、リードに登録してみます。

※リードは潜在的な顧客として認識された人や会社を登録するためのオブジェクトなのでリードの使い方としては誤りです。テストクラスでテストデータを作るくらいのイメージでやっています。

##  RandomUserAPIについて

[https://randomuser.me/api/](https://randomuser.me/api/)にアクセスすると、以下のようなJSONが返ってきます。

```json
{
    "results": [
        {
            "gender": "female",
            "name": {
                "title": "mademoiselle",
                "first": "olivia",
                "last": "lefebvre"
            },
            "location": {
                "street": "3819 avenue du château",
                "city": "st-barthélemy vd",
                "state": "basel-stadt",
                "postcode": 9956
            },
            "email": "olivia.lefebvre@example.com",
            "login": {
                "username": "greenmeercat253",
                "password": "opus",
                "salt": "5SGUWEbt",
                "md5": "559227b3c1722b2448e959658109a443",
                "sha1": "ef650448a47cef7e3d483cb8bf857ad1794bf0c3",
                "sha256": "0a264913c6bace08d8a6fd6c40a8dae6885042a50ae0c4add868b5570d36a65b"
            },
            "dob": "1967-12-07 06:42:54",
            "registered": "2007-05-13 04:55:23",
            "phone": "(441)-746-6416",
            "cell": "(704)-379-7611",
            "id": {
                "name": "AVS",
                "value": "756.JOKA.MGZG.65"
            },
            "picture": {
                "large": "https://randomuser.me/api/portraits/women/85.jpg",
                "medium": "https://randomuser.me/api/portraits/med/women/85.jpg",
                "thumbnail": "https://randomuser.me/api/portraits/thumb/women/85.jpg"
            },
            "nat": "CH"
        }
    ],
    "info": {
        "seed": "e9bc4458117a7c8d",
        "results": 1,
        "page": 1,
        "version": "1.1"
    }
}
```


デフォルトでは1人分の名前や電話番号といった情報が入りますが、[How to use](https://randomuser.me/documentation#howto)によると人数や出身地域を指定できるオプションがあるようなので、今回は人数、フォーマット、出身地域を指定してみます。

## やってみる

コード全体

```apex
public static void insertRandomUsers() {
	HttpRequest request = new HttpRequest();
	request.setEndpoint('https://randomuser.me/api/?results=10&format=JSON&nat=us');
	request.setMethod('GET');

	Http http = new Http();
	HttpResponse response = new HttpResponse();
	response = http.send(request);

	Map<String, Object> requestBody = new Map<String, Object>();
	Integer statusCode = response.getStatusCode();
	if (statusCode == 200) {
		requestBody = (Map<String, Object>)JSON.deserializeUntyped(response.getBody());
	} else {
		System.debug('Error : ' + statusCode + ' => ' + response.getBody());
		return;
	}

	List<Lead> leadList = new List<Lead>();
	for (Object result : (List<Object>)requestBody.get('results')) {
		Map<String, Object> user = (Map<String, Object>)result;
		Map<String, Object> name = (Map<String, Object>)user.get('name');

		Lead l = new Lead();
		l.firstName = String.valueOf(name.get('first'));
		l.lastName = String.valueOf(name.get('last'));
		l.phone = String.valueOf(user.get('phone'));
		l.Company = String.valueOf(user.get('email'));

		leadList.add(l);
	}
	insert leadList;
}
```

### GETでAPIを叩く

- `httpRequest`オブジェクトに対して`setEndPoint()`でURLを指定
- `httpRequest`オブジェクトに対して`setMethod()`でリクエストメソッドを指定
- `http`オブジェクトの`send()`メソッドで`httpResponse`オブジェクトが返ってくる

という流れになります。


```apex
HttpRequest request = new HttpRequest();
request.setEndpoint('https://randomuser.me/api/?results=10&format=JSON&nat=us');
request.setMethod('GET');

Http http = new Http();
HttpResponse response = new HttpResponse();
response = http.send(request);
```

### responseBodyの解析

GETした内容から名前や電話番号を取ってきてLeadオブジェクトにセットしたいわけですが、これが非常にめんどくさいです。
やり方としては`httpResponse.getBody()`でresponseの本体が取れるので、`JSON.deserializeUntyped()`しつつ`Map<String, Object>`にキャストしてあげると`"result"`キー、`"info"`キー以下の内容が`List<Object>`として扱えるようになります。

```apex
Map<String, Object> requestBody = (Map<String, Object>)JSON.deserializeUntyped(response.getBody());
// requestBody.keySet() : Set<String> => ('results', 'info')
// requestBody.get('results').values() : List<Object> => {'gender', 'name', 'location', ...}
// requestBody.get('info').values() : List<Object> => {'seed', 'results', 'page', 'version'}
```

`"gender"`やら`"name"`の値を扱うためには、`List<Object>`をループで回しながら、`String.valueOf()`で文字列に変換していくことになります。値がまた配列になっている場合は`Map<String, Object>`へのキャストというのをまたやってあげる必要があります。各階層について配列のキャストが必要になります。


### Leadへのセット

ループのなかでinsertしないのが基本ですね。

### 感想

responseBodyはせめて文字列のMapとして扱えるようにしてほしいです。指定したsObjectに変換できるとかあればもっとありがたいですが。