jeroen/jsonlite

Adding "undefined" as option to `na` in `toJSON`

jospueyo opened this issue · 3 comments

Currently, the function toJSON accepts 'null' or 'string' to handle NA values. I believe it will be useful adding a third option: "remove". Then, the NA values will be removed from the resulting json. Therefore, when converting a dataframe to a json with optional values, in the resulting body, the NA values will be removed from the body.

library(jsonlite)

body_df <- data.frame(
  a = c(1, 2, NA, 4),
  b = c("a", NA, "c", "d")
)

for (i in seq_len(nrow(body_df))){
  body <- list(
    a = body_df$a[[i]],
    b = body_df$b[[i]]
  ) |> 
    toJSON(auto_unbox = TRUE, na = "null", pretty = TRUE)
  cat(body, "\n\n")
}
#> {
#>   "a": 1,
#>   "b": "a"
#> } 
#> 
#> {
#>   "a": 2,
#>   "b": null
#> } 
#> 
#> {
#>   "a": null,
#>   "b": "c"
#> } 
#> 
#> {
#>   "a": 4,
#>   "b": "d"
#> }

Created on 2023-10-11 with reprex v2.0.2

Using remove, the json would be equivalent to:

#> {
#>   "a": 1,
#>   "b": "a"
#> } 
#> 
#> {
#>   "a": 2,
#> } 
#> 
#> {
#>   "b": "c"
#> } 
#> 
#> {
#>   "a": 4,
#>   "b": "d"
#> }
jeroen commented

This is already the default behavior when you don't pass any 'na' argument.

In my case, if I don't pass any argument, numbers are converted to "NA" and characters to null:

library(jsonlite)

body_df <- data.frame(
  a = c(1, 2, NA, 4),
  b = c("a", NA, "c", "d")
)

for (i in seq_len(nrow(body_df))){
  body <- list(
    a = body_df$a[[i]],
    b = body_df$b[[i]]
  ) |> 
    toJSON(auto_unbox = TRUE, pretty = TRUE)
  cat(body, "\n\n")
}
#> {
#>   "a": 1,
#>   "b": "a"
#> } 
#> 
#> {
#>   "a": 2,
#>   "b": null
#> } 
#> 
#> {
#>   "a": "NA",
#>   "b": "c"
#> } 
#> 
#> {
#>   "a": 4,
#>   "b": "d"
#> }

Created on 2023-10-11 with reprex v2.0.2

jeroen commented

Indeed, this only works if you convert the data frame to json directly, instead of converting it to lists as you did:

library(jsonlite)

body_df <- data.frame(
  a = c(1, 2, NA, 4),
  b = c("a", NA, "c", "d")
)

toJSON(body_df)

So this gives you a way of accomplishing that. As jsonlite is already complex enough, I'm don't want to add more options to do this for lists too.