Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/atime.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ jobs:
- uses: actions/checkout@v3
- uses: r-lib/actions/setup-r@v2
- uses: r-lib/actions/setup-r-dependencies@v2
- name: install RJSONIO for testing old package versions
run: Rscript -e "install.packages('RJSONIO', repos='https://cloud.r-project.org')"
- name: install package
run: R CMD INSTALL .
- uses: Anirban166/Autocomment-atime-results@v1.4.3
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ Depends:
Imports:
servr,
digest,
RJSONIO,
jsonlite,
grid,
gtable (>= 0.1.1),
MASS,
Expand Down
2 changes: 1 addition & 1 deletion NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,7 @@ export(xlim)
export(ylab)
export(ylim)
export(zeroGrob)
import(RJSONIO)
import(jsonlite)
import(data.table)
import(grid)
import(gtable)
Expand Down
17 changes: 15 additions & 2 deletions R/z_animint.R
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ storeLayer <- function(meta, g, g.data.varied){
#' @param chromote_height height of chromote window in pixels, default 2000 should be sufficient for most data viz, but can be increased if your data viz screenshot appears cropped too small.
#' @return invisible list of ggplots in list format.
#' @export
#' @import RJSONIO
#' @import jsonlite
#' @importFrom utils browseURL head packageVersion str tail
#' write.table
#' @example inst/examples/animint2dir.R
Expand Down Expand Up @@ -637,7 +637,20 @@ animint2dir <- function
export.data[[export.name]] <- meta[[export.name]]
}
}
json <- RJSONIO::toJSON(export.data)
## Convert named vectors to lists for jsonlite compatibility (issue #193)
## RJSONIO converts named vectors to JSON objects, jsonlite converts to arrays
## This helper ensures jsonlite produces the same output as RJSONIO
convert_for_json <- function(x) {
if (is.list(x)) {
lapply(x, convert_for_json)
} else if (is.atomic(x) && !is.null(names(x))) {
as.list(x)
} else {
x
}
}
export.data <- convert_for_json(export.data)
json <- jsonlite::toJSON(export.data, auto_unbox = TRUE, force = TRUE, null = "null")
cat(json, file = file.path(out.dir, "plot.json"))
if (open.browser) {
if (identical(getOption("animint.browser"),"browseURL")) {
Expand Down
2 changes: 1 addition & 1 deletion R/z_pages.R
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ update_gallery <- function(gallery_path="~/R/gallery"){
}
local.json <- tempfile()
download.file(viz_url("plot.json"), local.json)
jlist <- RJSONIO::fromJSON(local.json)
jlist <- jsonlite::fromJSON(local.json, simplifyVector = FALSE)
to.check <- c(
source="URL of data viz source code",
title="string describing the data viz")
Expand Down
2 changes: 1 addition & 1 deletion inst/examples/gps.R
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ if(!file.exists(ile_de_france.json)){
ile_de_france.json)
download.file(u, ile_de_france.json)
}
##ile_de_france.list <- RJSONIO::fromJSON(ile_de_france.json)
##ile_de_france.list <- jsonlite::fromJSON(ile_de_france.json)
ile_de_france.sf <- geojsonsf::geojson_sf(ile_de_france.json)
names(ile_de_france.sf)
class(ile_de_france.sf)
Expand Down
209 changes: 209 additions & 0 deletions tests/testthat/test-compiler-json-migration.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
acontext("JSON migration validation for issue #193")
## This test validates jsonlite compatibility for RJSONIO migration.
## See https://github.com/animint/animint2/issues/193
## RJSONIO is no longer maintained on CRAN, so we migrate to jsonlite.
## The key requirement: auto_unbox=TRUE makes jsonlite match RJSONIO behavior.
test_that("jsonlite encodes single values without array wrapping when auto_unbox=TRUE", {
data <- list(geom="point", size=3, enabled=TRUE)
json_str <- jsonlite::toJSON(data, auto_unbox=TRUE)
expect_false(grepl('\\["point"\\]', json_str))
expect_false(grepl('\\[3\\]', json_str))
expect_true(grepl('"geom"\\s*:\\s*"point"', json_str))
expect_true(grepl('"size"\\s*:\\s*3', json_str))
})
test_that("jsonlite preserves vector arrays correctly", {
data <- list(x=c(1,2,3), labels=c("a","b","c"))
json_str <- jsonlite::toJSON(data, auto_unbox=TRUE)
parsed <- jsonlite::fromJSON(json_str, simplifyVector=FALSE)
expect_equal(length(parsed$x), 3)
expect_equal(length(parsed$labels), 3)
expect_equal(parsed$x[[1]], 1)
expect_equal(parsed$labels[[2]], "b")
})
test_that("jsonlite handles nested list structures like plot.json geoms", {
geom_data <- list(
geom1_point_plot=list(
geom="point",
classed="geom1_point_plot",
aes=list(x="x", y="y"),
params=list(na.rm=FALSE, size=3),
chunks=1,
total=1
)
)
json_str <- jsonlite::toJSON(geom_data, auto_unbox=TRUE)
parsed <- jsonlite::fromJSON(json_str, simplifyVector=FALSE)
expect_equal(parsed$geom1_point_plot$geom, "point")
expect_equal(parsed$geom1_point_plot$aes$x, "x")
expect_equal(parsed$geom1_point_plot$params$size, 3)
expect_equal(parsed$geom1_point_plot$chunks, 1)
})
test_that("jsonlite handles plot layout data with boolean arrays", {
layout_data <- list(
PANEL=c("1","2","3","4"),
ROW=c(1,1,2,2),
COL=c(1,2,1,2),
AXIS_X=c(FALSE,FALSE,TRUE,TRUE),
AXIS_Y=c(TRUE,FALSE,TRUE,FALSE)
)
json_str <- jsonlite::toJSON(layout_data, auto_unbox=TRUE)
parsed <- jsonlite::fromJSON(json_str, simplifyVector=FALSE)
expect_equal(length(parsed$PANEL), 4)
expect_equal(parsed$AXIS_X[[3]], TRUE)
expect_equal(parsed$AXIS_Y[[2]], FALSE)
})
test_that("jsonlite handles axis tick data arrays", {
axis_data <- list(
x=c(2.5, 5, 7.5, 10),
xlab=c("2.5", "5.0", "7.5", "10.0"),
xrange=c(0.55, 10.45),
xline=TRUE,
xticks=TRUE
)
json_str <- jsonlite::toJSON(axis_data, auto_unbox=TRUE)
parsed <- jsonlite::fromJSON(json_str, simplifyVector=FALSE)
expect_equal(length(parsed$x), 4)
expect_equal(parsed$xrange[[1]], 0.55)
expect_equal(parsed$xline, TRUE)
})
test_that("jsonlite handles nested grid location arrays", {
grid_data <- list(
grid_major=list(
colour="#FFFFFF",
size=0.5,
loc=list(
x=list(c(2.5,5,7.5,10), c(2.5,5,7.5,10)),
y=list(c(2.5,5,7.5,10), c(2.5,5,7.5,10))
)
)
)
json_str <- jsonlite::toJSON(grid_data, auto_unbox=TRUE)
parsed <- jsonlite::fromJSON(json_str, simplifyVector=FALSE)
expect_equal(parsed$grid_major$colour, "#FFFFFF")
expect_equal(length(parsed$grid_major$loc$x), 2)
expect_equal(length(parsed$grid_major$loc$x[[1]]), 4)
})
test_that("jsonlite handles selector structures for interactivity", {
selector_data <- list(
selectors=list(
year=list(selected="2000", type="single")
),
first=list(year="2000"),
time=list(ms=2000, variable="year"),
duration=list(year=500)
)
json_str <- jsonlite::toJSON(selector_data, auto_unbox=TRUE)
parsed <- jsonlite::fromJSON(json_str, simplifyVector=FALSE)
expect_equal(parsed$selectors$year$selected, "2000")
expect_equal(parsed$time$ms, 2000)
expect_equal(parsed$duration$year, 500)
})
test_that("jsonlite handles empty lists and structures", {
data <- list(legend=list(), panel_border=list(), selectors=list())
json_str <- jsonlite::toJSON(data, auto_unbox=TRUE)
parsed <- jsonlite::fromJSON(json_str, simplifyVector=FALSE)
expect_true("legend" %in% names(parsed))
expect_true("panel_border" %in% names(parsed))
expect_equal(length(parsed$legend), 0)
})
test_that("jsonlite handles strip and facet data", {
strip_data <- list(
strips=list(
top=c("A","B","C","D"),
right=c(""),
n=list(top=2, right=0)
)
)
json_str <- jsonlite::toJSON(strip_data, auto_unbox=TRUE)
parsed <- jsonlite::fromJSON(json_str, simplifyVector=FALSE)
expect_equal(length(parsed$strips$top), 4)
expect_equal(parsed$strips$n$top, 2)
})
test_that("jsonlite handles panel styling parameters", {
style_data <- list(
panel_background=list(
fill="#EBEBEB",
colour="transparent",
size=0.5,
linetype=1
)
)
json_str <- jsonlite::toJSON(style_data, auto_unbox=TRUE)
parsed <- jsonlite::fromJSON(json_str, simplifyVector=FALSE)
expect_equal(parsed$panel_background$fill, "#EBEBEB")
expect_equal(parsed$panel_background$size, 0.5)
})
test_that("jsonlite handles chunk_info metadata", {
chunk_data <- list(
chunk_info=list(
"geom1_point_chunk1.tsv"=list(bytes=258, rows=40)
)
)
json_str <- jsonlite::toJSON(chunk_data, auto_unbox=TRUE)
parsed <- jsonlite::fromJSON(json_str, simplifyVector=FALSE)
tsv_info <- parsed$chunk_info[["geom1_point_chunk1.tsv"]]
expect_equal(tsv_info$bytes, 258)
expect_equal(tsv_info$rows, 40)
})
test_that("jsonlite round-trip preserves complete export.data structure", {
## Mimics the export.data structure from R/z_animint.R line 631-639
export_data <- list(
geoms=list(
geom1_point_scatter=list(
geom="point",
classed="geom1_point_scatter",
aes=list(x="x", y="y"),
params=list(na.rm=FALSE, size=3),
types=list(x="numeric", y="numeric"),
chunk_order=list(),
nest_order=c("PANEL"),
subset_order=c("PANEL"),
chunks=1,
total=1
)
),
time=list(ms=2000, variable="year"),
duration=list(year=500),
selectors=list(year=list(selected="2000", type="single")),
plots=list(
scatter=list(
panel_margin_lines=0.25,
legend=list(),
xtitle="X Axis",
ytitle="Y Axis",
title="Test Plot",
options=list(width=600, height=400),
geoms=c("geom1_point_scatter")
)
)
)
json_str <- jsonlite::toJSON(export_data, auto_unbox=TRUE)
parsed <- jsonlite::fromJSON(json_str, simplifyVector=FALSE)
expect_equal(parsed$geoms$geom1_point_scatter$geom, "point")
expect_equal(parsed$plots$scatter$title, "Test Plot")
expect_equal(parsed$time$ms, 2000)
expect_equal(parsed$selectors$year$selected, "2000")
expect_equal(parsed$plots$scatter$options$width, 600)
})
test_that("jsonlite output is valid JSON parseable by JavaScript", {
## This test ensures the JSON string format is valid
data <- list(
plot="scatter",
data=list(x=c(1,2,3), y=c(4,5,6)),
mapping=list(x="x", y="y")
)
json_str <- jsonlite::toJSON(data, auto_unbox=TRUE)
## Valid JSON should start with { and end with }
expect_true(grepl("^\\s*\\{", json_str))
expect_true(grepl("\\}\\s*$", json_str))
## Should not have R-specific artifacts
expect_false(grepl("NA", json_str))
expect_false(grepl("NULL", json_str))
})
test_that("jsonlite handles numeric precision for axis ranges", {
data <- list(xrange=c(0.55, 10.45), yrange=c(-3.14159, 2.71828))
json_str <- jsonlite::toJSON(data, auto_unbox=TRUE)
parsed <- jsonlite::fromJSON(json_str, simplifyVector=FALSE)
expect_equal(parsed$xrange[[1]], 0.55, tolerance=1e-10)
expect_equal(parsed$yrange[[1]], -3.14159, tolerance=1e-5)
})
6 changes: 3 additions & 3 deletions tests/testthat/test-renderer3-chunk-NA-separate-lines.R
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ test_that("geom2 common chunk with group=1 and color common", {
geom1.dt <- fread(geom1.tsv)
expect_equal(sum(is.na(geom1.dt)), 0)
plot.json <- file.path("animint-htmltest", "plot.json")
json.list <- RJSONIO::fromJSON(plot.json)
json.list <- jsonlite::fromJSON(plot.json, simplifyVector = FALSE)
group_num <- json.list$geoms$geom2_path_selected$chunks[["San Marcos"]]
geom.tsv <- sprintf("animint-htmltest/geom2_path_selected_chunk%d.tsv", group_num)
geom.dt <- fread(geom.tsv)
Expand Down Expand Up @@ -125,7 +125,7 @@ test_that("geom2 common chunk with no group and color common", {
geom1.dt <- fread(geom1.tsv)
expect_equal(sum(is.na(geom1.dt)), 0)
plot.json <- file.path("animint-htmltest", "plot.json")
json.list <- RJSONIO::fromJSON(plot.json)
json.list <- jsonlite::fromJSON(plot.json, simplifyVector = FALSE)
group_num <- json.list$geoms$geom2_path_selected$chunks[["San Marcos"]]
geom.tsv <- sprintf("animint-htmltest/geom2_path_selected_chunk%d.tsv", group_num)
geom.dt <- fread(geom.tsv)
Expand Down Expand Up @@ -176,7 +176,7 @@ test_that("geom2 common chunk ok with group=1 and only x common", {
geom1.dt <- fread(geom1.tsv)
expect_equal(sum(is.na(geom1.dt)), 0)
plot.json <- file.path("animint-htmltest", "plot.json")
json.list <- RJSONIO::fromJSON(plot.json)
json.list <- jsonlite::fromJSON(plot.json, simplifyVector = FALSE)
group_num <- json.list$geoms$geom2_path_selected$chunks[["San Marcos"]]
geom.tsv <- sprintf("animint-htmltest/geom2_path_selected_chunk%d.tsv", group_num)
geom.dt <- fread(geom.tsv)
Expand Down
Loading