jsonlite icon indicating copy to clipboard operation
jsonlite copied to clipboard

For an NA value in a lists column in a data frame, toJSON outputs an empty list (instead of nothing)

Open apsalverda opened this issue 3 years ago • 2 comments

When using toJSON() on a data frame, default behavior for NA values is that no field is included in the JSON record (see also section 2.4.3 in The jsonlite Package: A Practical and Consistent Mapping Between JSON Data and R Objects). I like this! However, this doesn't appear to work for list columns:

test = structure(list(
  foo = c(NA, TRUE, FALSE),
  bar = c("Aladdin", NA, "Mario"),
  nested_list = c(list(list(c("cat", "dog"), c("tree"))), 
                  list(list(c("cat", "dog"), c("tree"))), 
                  NA)),
  row.names = c(NA, -3L), class = "data.frame")

test
#     foo     bar    nested_list
# 1    NA Aladdin cat, dog, tree
# 2  TRUE    <NA> cat, dog, tree
# 3 FALSE   Mario             NA
is.na(test)
#        foo   bar nested_list
# [1,]  TRUE FALSE       FALSE
# [2,] FALSE  TRUE       FALSE
# [3,] FALSE FALSE        TRUE

jsonlite::toJSON(test, pretty = TRUE)
# [
#   {
#     "bar": "Aladdin",
#     "nested_list": [
#       ["cat", "dog"],
#       ["tree"]
#     ]
#   },
#   {
#     "foo": true,
#     "nested_list": [
#       ["cat", "dog"],
#       ["tree"]
#     ]
#   },
#   {
#     "foo": false,
#     "bar": "Mario",
#     "nested_list": [null] # NOTE: nested_list = NA in data frame above
#   }
# ]

On a related note, I would propose a small change to the "na" argument. From ?jsonlite::toJSON:

na how to print NA values: must be one of 'null' or 'string'. Defaults are class specific

I think that it would be helpful for "na" to also have the possible value "skip", which would result in no field in the JSON record for NA values.

apsalverda avatar Feb 05 '22 23:02 apsalverda

It would be helpful if a similar logic could be implemented for NULLs. In the following example, the empty _children-entry causes an error for toastui::datagrid().

json_raw <- paste0('
[
    {
        "id": 549731,
        "name": "Beautiful Lies",
        "artist": "Birdy",
        "listenCount": 5000,
        "_children": [
            {
                "id": 491379,
                "name": "Chaos And The Calm",
                "artist": "James Bay",
                "listenCount": 5000
            }
        ]
    },
    {
        "id": 436461,
        "name": "X",
        "artist": "Ed Sheeran",
        "listenCount": 5000
    }
]')

jsonlite::toJSON(jsonlite::fromJSON(json_raw), pretty = TRUE)
#> [
#>   {
#>     "id": 549731,
#>     "name": "Beautiful Lies",
#>     "artist": "Birdy",
#>     "listenCount": 5000,
#>     "_children": [
#>       {
#>         "id": 491379,
#>         "name": "Chaos And The Calm",
#>         "artist": "James Bay",
#>         "listenCount": 5000
#>       }
#>     ]
#>   },
#>   {
#>     "id": 436461,
#>     "name": "X",
#>     "artist": "Ed Sheeran",
#>     "listenCount": 5000,
#>     "_children": {}
#>   }
#> ]

thohan88 avatar Jun 15 '22 10:06 thohan88

Just saw that this was also requested in #143

thohan88 avatar Jun 15 '22 11:06 thohan88