Skip to content

Commit

Permalink
close rstudio#973, add excel serializer/parser
Browse files Browse the repository at this point in the history
  • Loading branch information
Bill Evans committed Jan 23, 2025
1 parent f4230ed commit 538cb6f
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 1 deletion.
4 changes: 3 additions & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ Suggests:
rapidoc,
sf,
ragg,
svglite
svglite,
readxl,
writexl
RoxygenNote: 7.3.2
Collate:
'async.R'
Expand Down
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export(include_rmd)
export(is_plumber)
export(options_plumber)
export(parser_csv)
export(parser_excel)
export(parser_feather)
export(parser_form)
export(parser_geojson)
Expand Down Expand Up @@ -80,6 +81,7 @@ export(serializer_cat)
export(serializer_content_type)
export(serializer_csv)
export(serializer_device)
export(serializer_excel)
export(serializer_feather)
export(serializer_format)
export(serializer_geojson)
Expand Down
6 changes: 6 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@
* Added support for graphic devices provided by ragg and svglite (@thomasp85 #964)
* `parse_rds()`, `parse_feather()`, and `parse_parquet()` no longer writes data to disk during parsing (@thomasp85, #942)

* New serializers
* `serializer_excel()`: Return an object serialized by `writexl::write_xlsx` (@r2evans, #973).

* New request body parsers
* `parser_excel()`: Parse request body as an excel workbook using `readxl::read_excel` (@r2evans, #973). This defaults to loading in the first worksheet only, you can use `@parse excel list(sheet=NA)` to import all worksheets. This always returns a list of frames, even if just one worksheet.

# plumber 1.2.2

* Allow to set plumber options using environment variables `?options_plumber`. (@meztez #934)
Expand Down
33 changes: 33 additions & 0 deletions R/parse-body.R
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,38 @@ parser_parquet <- function(...) {
}
}

# readxl's default behavior is to read only one worksheet at a time; in order for an endpoint to
# read multiple worksheets, its documentation suggests to iterate over discovered names (c.f.,
# https://readxl.tidyverse.org/articles/readxl-workflows.html#iterate-over-multiple-worksheets-in-a-workbook);
# for this reason, this parser detects an NA in the 'sheet=' argument and replaces it with all
# worksheet names found in the workbook

#' @describeIn parsers excel parser. See [readxl::read_excel()] for more details. (Defaults to reading in the first worksheet only, use `@parser excel list(sheet=NA)` to read in all worksheets.)
#' @export
parser_excel <- function(..., sheet = NULL) {
if (!requireNamespace("readxl", quietly = TRUE)) {
stop("`readxl` must be installed for `parser_excel` to work")
}
parse_fn <- parser_read_file(function(tmpfile) {
if (is.null(sheet)) {
# we have to hard-code this since lapply won't iterate if NULL
sheet <- 1L
} else if (anyNA(sheet)) {
sheet <- readxl::excel_sheets(tmpfile)
}
if (is.character(sheet)) names(sheet) <- sheet
out <- suppressWarnings(
lapply(sheet, function(sht) {
readxl::read_excel(path = tmpfile, sheet = sht, ...)
})
)
out
})
function(value, ...) {
parse_fn(value)
}
}

#' @describeIn parsers Octet stream parser. Returns the raw content.
#' @export
parser_octet <- function() {
Expand Down Expand Up @@ -588,6 +620,7 @@ register_parsers_onLoad <- function() {
register_parser("rds", parser_rds, fixed = "application/rds")
register_parser("feather", parser_feather, fixed = c("application/vnd.apache.arrow.file", "application/feather"))
register_parser("parquet", parser_parquet, fixed = "application/vnd.apache.parquet")
register_parser("excel", parser_excel, fixed = c("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "application/vnd.ms-excel"))
register_parser("text", parser_text, fixed = "text/plain", regex = "^text/")
register_parser("tsv", parser_tsv, fixed = c("application/tab-separated-values", "text/tab-separated-values"))
# yaml types: https://stackoverflow.com/a/38000954/591574
Expand Down
17 changes: 17 additions & 0 deletions R/serializer.R
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,22 @@ serializer_parquet <- function(type = "application/vnd.apache.parquet") {
)
}

#' @describeIn serializers excel serializer. See also: [writexl::write_xlsx()]
#' @export
serializer_excel <- function(..., type = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") {
if (!requireNamespace("writexl", quietly = TRUE)) {
stop("The writexl package is not available but is required in order to use the writexl serializer",
call. = FALSE)
}

serializer_write_file(
fileext = ".xlsx",
type = type,
write_fn = function(val, tmpfile) {
writexl::write_xlsx(x = val, path = tmpfile, ...)
}
)
}

#' @describeIn serializers YAML serializer. See also: [yaml::as.yaml()]
#' @export
Expand Down Expand Up @@ -693,6 +709,7 @@ add_serializers_onLoad <- function() {
register_serializer("tsv", serializer_tsv)
register_serializer("feather", serializer_feather)
register_serializer("parquet", serializer_parquet)
register_serializer("excel", serializer_excel)
register_serializer("yaml", serializer_yaml)
register_serializer("geojson", serializer_geojson)

Expand Down
5 changes: 5 additions & 0 deletions man/parsers.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions man/serializers.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 538cb6f

Please sign in to comment.