diff --git a/R/json-opts.R b/R/json-opts.R index 120a1d7..e05dae3 100644 --- a/R/json-opts.R +++ b/R/json-opts.R @@ -169,6 +169,7 @@ yyjson_write_flag <- list( #' be stored in this type. #' @param df_missing_list_elem R value to use when elements are missing in list #' columns in data.frames. Default: NULL +#' @param any_single_null_elem R value to use for any single NULL element. Default: NULL #' @param obj_of_arrs_to_df logical. Should a named list of equal-length #' vectors be promoted to a data.frame? Default: TRUE. If FALSE, then #' result will be left as a list. @@ -204,6 +205,7 @@ yyjson_write_flag <- list( opts_read_json <- function( promote_num_to_string = FALSE, df_missing_list_elem = NULL, + any_single_null_elem = NULL, obj_of_arrs_to_df = TRUE, arr_of_objs_to_df = TRUE, str_specials = c('string', 'special'), @@ -217,6 +219,7 @@ opts_read_json <- function( list( promote_num_to_string = isTRUE(promote_num_to_string), df_missing_list_elem = df_missing_list_elem, + any_single_null_elem = any_single_null_elem, obj_of_arrs_to_df = isTRUE(obj_of_arrs_to_df), arr_of_objs_to_df = isTRUE(arr_of_objs_to_df), length1_array_asis = isTRUE(length1_array_asis), diff --git a/man/opts_read_json.Rd b/man/opts_read_json.Rd index 194f817..7888b96 100644 --- a/man/opts_read_json.Rd +++ b/man/opts_read_json.Rd @@ -7,6 +7,7 @@ opts_read_json( promote_num_to_string = FALSE, df_missing_list_elem = NULL, + any_single_null_elem = NULL, obj_of_arrs_to_df = TRUE, arr_of_objs_to_df = TRUE, str_specials = c("string", "special"), @@ -28,6 +29,8 @@ if you want to emulate the behaviour of \code{jsonlite::fromJSON()}} \item{df_missing_list_elem}{R value to use when elements are missing in list columns in data.frames. Default: NULL} +\item{any_single_null_elem}{R value to use for any single NULL element. Default: NULL} + \item{obj_of_arrs_to_df}{logical. Should a named list of equal-length vectors be promoted to a data.frame? Default: TRUE. If FALSE, then result will be left as a list.} diff --git a/src/R-yyjson-parse.c b/src/R-yyjson-parse.c index 15e5e7c..699fd6e 100644 --- a/src/R-yyjson-parse.c +++ b/src/R-yyjson-parse.c @@ -33,6 +33,7 @@ parse_options create_parse_options(SEXP parse_opts_) { parse_options opt = { .int64 = INT64_AS_STR, .df_missing_list_elem = R_NilValue, + .any_single_null_elem = R_NilValue, .obj_of_arrs_to_df = true, .arr_of_objs_to_df = true, .length1_array_asis = false, @@ -74,6 +75,8 @@ parse_options create_parse_options(SEXP parse_opts_) { } } else if (strcmp(opt_name, "df_missing_list_elem") == 0) { opt.df_missing_list_elem = val_; + } else if (strcmp(opt_name, "any_single_null_elem") == 0) { + opt.any_single_null_elem = val_; } else if (strcmp(opt_name, "yyjson_read_flag") == 0) { for (unsigned int idx = 0; idx < length(val_); idx++) { opt.yyjson_read_flag |= (unsigned int)INTEGER(val_)[idx]; @@ -1794,7 +1797,8 @@ SEXP json_as_robj(yyjson_val *val, parse_options *opt) { res_ = PROTECT(mkString(yyjson_get_str(val))); nprotect++; break; case YYJSON_TYPE_NULL: - res_ = R_NilValue; + // res_ = R_NilValue; + res_ = opt->any_single_null_elem; break; default: warning("json_as_robj(): unhandled: %s\n", yyjson_get_type_desc(val)); diff --git a/src/R-yyjson-parse.h b/src/R-yyjson-parse.h index 12ae38d..205dd96 100644 --- a/src/R-yyjson-parse.h +++ b/src/R-yyjson-parse.h @@ -97,6 +97,7 @@ typedef struct { unsigned int int64; SEXP df_missing_list_elem; + SEXP any_single_null_elem; bool obj_of_arrs_to_df; bool arr_of_objs_to_df; bool length1_array_asis; diff --git a/tests/testthat/test-dataframe.R b/tests/testthat/test-dataframe.R index eb7e0b4..4120ef0 100644 --- a/tests/testthat/test-dataframe.R +++ b/tests/testthat/test-dataframe.R @@ -95,7 +95,18 @@ test_that("array of nested json objects to data.frame", { ) df$a <- list(10.1, "greg", NA) expect_equal(test, df) - + + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + #' Ordinary array with "NA" for missing values + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + test <- read_json_str('{"x":null,"y":null,"z":3}',opts=yyjsonr::opts_read_json(any_single_null_elem=NA,arr_of_objs_to_df = T)) + test + df <- list( + x = NA, + y = NA, + z=3 + ) + expect_equal(test, df) #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #' Data.frame with explicit NULL