Recently I was working on an R package that had historically used both rjson and jsonlite to serialize R objects to JSON (before sending it off to an API). In this case we wanted to remove the rjson depdency, which was only used in a few places.
The most noticable hiccup I encountered while porting the code was during encoding of parameter lists generated in R, which looked something like
params <- list(key1 = "param", key2 = NULL,
key3 = c("paired", "params"))
In this case, rjson produced exactly what our API was looking for:
cat(rjson::toJSON(params))
#> {"key1":"param","key2":null,"key3":["paired","params"]}
But by default the jsonlite package will behave very differently:
jsonlite::toJSON(params)
#> {"key1":["param"],"key2":{},"key3":["paired","params"]}
(That is, the NULL
has been converted to an empty dictionary and the key1
parameter has been turned into a one-element array.)
Fortunately, jsonlite is quite configurable. Vectors of a single element can
be converted into bare values by passing the auto_unbox
parameter to
toJSON()
(or handled individually with unbox()
), and handling of NULL
values can be customized with the null
paramter.
To get the jsonlite equivalent to the rjson output above, we used
jsonlite::toJSON(params, auto_unbox = TRUE, null = "null")
#> {"key1":"param","key2":null,"key3":["paired","params"]}
During the transition, I ran a number of tests to ensure that the conversions
were equivalent. Since jsonlite produces an object of class "json"
, you’ll
need to coerce to character to make things like all.equal()
work correctly:
a <- rjson::toJSON(params)
b <- jsonlite::toJSON(params, auto_unbox = TRUE, null = "null")
all.equal(a, b)
#> [1] "Attributes: < target is NULL, current is list >"
#> [2] "target is character, current is json"
all.equal(a, as.character(b))
#> [1] TRUE
Technical merits of each respective package aside, I think it’s likely that some existing rjson code will migrate to jsonlite in the future. Hopefully this post helps make that process a smooth one.