メモ:RでJSONファイルを読み書きするときはjsonlite::read_json(), jsonlite::write_json()

jsonlite 1.2で、read_json()write_json()という関数が追加されました。

Update jsonlite 1.2

ディスクから直接読み書きするから速い、らしい。そんなパフォーマンスを気にするほどでかいJSONファイルを扱ったことないけどいちおうメモ。

使い方

書き出し

上のブログ記事であったように、irisをファイルを書き出してみます(irisはdata.frameですが、listでもあります)。write_json()を使います。

library(jsonlite)

tmp <- tempfile()
write_json(iris, tmp)

このファイルの中身はどうなっているでしょう。ファイルの中身を見るにはreadrパッケージのread_file()とかread_lines()あたりが便利です。

readr::read_file(tmp)
#> [1] "[{\"Sepal.Length\":5.1,\"Sepal.Width\":3.5,\"Petal.Length\":1.4,\"Petal.Width\":0.2,\"Species\":\"setosa\"},... <truncated>

なんかJSONっぽい感じになっていますね。(わかりにくいですが、ダブルクオートが\でエスケープされているのは、Rがprintするときに一番外側を"で囲っているためです)

write_json()read_json()は、追加の引数を指定すると、それぞれ内部で使われているtoJSON()fromJSON()に渡されます。たとえば、pretty = TRUEを指定すると、整形されたJSONになります。

write_json(iris, tmp, pretty = FALSE)
cat(readr::read_lines(tmp, n_max = 10L), sep = "\n")
#> [
#>   {
#>     "Sepal.Length": 5.1,
#>     "Sepal.Width": 3.5,
#>     "Petal.Length": 1.4,
#>     "Petal.Width": 0.2,
#>     "Species": "setosa"
#>   },
#>   {
#>     "Sepal.Length": 4.9,

読み込み

read_json()を使います。

str(read_json(tmp))
#> List of 150
#>  $ :List of 5
#>   ..$ Sepal.Length: num 5.1
#>   ..$ Sepal.Width : num 3.5
#>   ..$ Petal.Length: num 1.4
#>   ..$ Petal.Width : num 0.2
#>   ..$ Species     : chr "setosa"
#>  $ :List of 5
#>   ..$ Sepal.Length: num 4.9
#>   ..$ Sepal.Width : int 3
#>   ..$ Petal.Length: num 1.4
#>   ..$ Petal.Width : num 0.2
#>   ..$ Species     : chr "setosa"
#> 
#> ...snip...

冒頭のrOpenSciの記事にあったように、ここはfromJSON()と挙動が違い、デフォルトではsimplifyされません。simplifyVector = TRUEを明示的に指定するとsimplifyされます。

str(read_json(tmp, simplifyVector = TRUE))
#> 'data.frame':    150 obs. of  5 variables:
#>  $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
#>  $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
#>  $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
#>  $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
#>  $ Species     : chr  "setosa" "setosa" "setosa" "setosa" ...

なぜfromJSON()と挙動が違うのか

なんで挙動変えたんだろう...と思ってissueを見ると、data.frameに変換するときは、なるべく暗黙に変換するのではなくreadrパッケージでやってるみたいに型を指定したい、という要望があって、

OK so I guess jsonlite should only do the parsing, and than you can do the simplification, coercion, tidyfication and transformations in another package. (https://github.com/jeroenooms/jsonlite/issues/161#issuecomment-268513054)

ということでjsonliteでは余計な変換はしない、という話になったらしいです。まあこれはこれで理解できる気がする。