jsonliteで要素ひとつだけのベクトルをうまくtoJSON()する

追記(2014/10/02): toJSON()にはauto_unboxというオプションがあって、それをTRUEにしとけば自動でいい感じにしてくれると教えてもらいました。

jsonliteで要素ひとつだけのベクトルをうまくtoJSON()するにはauto_unboxを使えばいい - Technically, technophobic.

ということで、以下で書いている、手動でunboxするのは無駄な努力でした。。
auto_unbox使いましょう。


前回(jsonliteにもやっとした - Technically, technophobic.)書いたけど、 jsonliteはすばらしいけど要素ひとつだけのベクトルをうまくtoJSON()できない。

こんな感じ。

> library(jsolite)
> json_str <- '{ "x" : 1 }'
> json_obj <- fromJSON(json_str)
> cat(toJSON(json_obj))
{ "x" : [ 1 ] }

で、なんかやり方ないかなーと探してたら、unboxという関数があるらしい。

OpenCPU - Release of jsonlite 0.9.4

The new unbox function was requested several users. It can be used to force atomic vectors of length 1 to be encoded as a JSON scalar rather than an array.

こんな感じ。

# unboxを使わないと
> cat(toJSON(list(x=1)))
{ "x" : [ 1 ] }

# unboxを使うと…
> cat(toJSON(list(x=unbox(1))))
{ "x" : 1 }

おおー、これこれ。
ということで、まあできそうです。お騒がせしました。

ネストしまくったlistに再帰的に関数を適用するには、rapplyという関数があるらしいです。
(存在を忘れてた。。)

こんな感じ。

> a <- list(a=1, b=c(1,2,3), c=list(x=1, y=c(2,3)))
> (a_unboxed <- rapply(a, function(x) if(length(x) == 1) unbox(x) else x, how="list"))
$a
[1] 1
attr(,"class")
[1] "scalar"  "numeric"

$b
[1] 1 2 3

$c
$c$x
[1] 1
attr(,"class")
[1] "scalar"  "numeric"

$c$y
[1] 2 3


> cat(toJSON(a_unboxed))
{ "a" : 1, "b" : [ 1, 2, 3 ], "c" : { "x" : 1, "y" : [ 2, 3 ] } }

(ちなみにこれ、if...else...構文でなくifelse()関数でやろうとしたらうまくいきません。 なんででしょう。。)

> (a_unboxed <- rapply(a, function(x) ifelse(length(x) == 1, unbox(x), x), how="list"))
$a
[1] 1

$b
[1] 1

$c
$c$x
[1] 1

$c$y
[1] 2

(追記) 理由分かりました。ブログを書きました↓

ifelse()とif-else構文の違い - Technically, technophobic.