メモ:httrでJSON形式のリクエストを送るときのコツ

httrパッケージでJSON形式のリクエストを送るときは、bodyに送りたい内容を、encode"json"を指定します。

たとえばこんな感じです。

httr::POST("example.com",
           body = list(id = 1),
           encode = "json")

このとき、bodyに渡されたリストは最終的にどのようにJSONに変換されるのか気になったのでメモ。

ソースを読む

httr::VERB()のこの行でbody_config()という関数が呼ばれています。

httr/http-verb.R at 7261a525ede1beb9880d1ce2e2009d27c8e8bef7 · hadley/httr · GitHub

body_config()を見ると、encode="json"のときにbodyに対して行われる操作は2つだけです。

httr/body.R at master · hadley/httr · GitHub

  1. compact()NULLの要素を取り除く(これはencode="json"以外でも同じ)。compact()の定義はここ:httr/utils.r at bdafe6d736ac977cd0a4230044688a64073ad9bb · hadley/httr · GitHub
  2. jsonlite::toJSON(x, auto_unbox = TRUE)JSONに変換。

ポイント1:NULLは取り除かれる

勝手に取り除いてくれるので、NULLな要素もとりあえずリストに突っ込んでしまって問題ありません。

some_api <- function(param1 = NULL, param2 = NULL, param3 = NULL) {
  httr::GET("example.com",
            body = list(
              param1 = param1,
              param2 = param2,
              param3 = param3
            ),
            encode = "json")
}

という関数をつくれば、

some_api(param1 = 1)とやれば{"param1": 1}が、some_api(param2 = 2, param3 = 4)とやれば{"param2": 2, "param3": 4}というように、指定したパラメータだけが送られるようにできます。

ポイント2: unboxしたくないやつにはI()を使う

たとえば、こんな感じのリクエストを送りたいとします。

{
  "id": 1,
  "values": [1, 2, 3, 4]
}

これは、何も考えずに同じものをlistで書けばうまくいきます。

library(jsonlite)
toJSON(list(id = 1, values = c(1,2,3,4)), auto_unbox = TRUE)
#> {"id":1,"values":[1,2,3,4]} 

しかし、valuesの長さが1のときは、valuesの方まで勝手にunboxされてしまうので困ります。

toJSON(list(id = 1, values = 1), auto_unbox = TRUE)
#> {"id":1,"values":1} 

こういうときは、I()で囲むとunboxされなくなります。

toJSON(list(id = 1, values = I(1)), auto_unbox = TRUE)
#> {"id":1,"values":[1]}

ちなみに、httr:::body_config()の中ではauto_unbox = TRUEが埋め込まれているのでこの方法しかないですが、自分でJSONを組み立てるときは、逆にauto_unbox = FALSEにして明示的にunbox()するという手もあります。

toJSON(list(id = unbox(1), values = 1), auto_unbox = FALSE)
#> {"id":1,"values":[1]}