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
compact()でNULLの要素を取り除く(これはencode="json"以外でも同じ)。compact()の定義はここ:httr/utils.r at bdafe6d736ac977cd0a4230044688a64073ad9bb · hadley/httr · GitHubjsonlite::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]}