-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
switch from RJSONIO to jsonlite #572
Comments
It would probably be good to do that eventually, but as RJSONIO and jsonlite have slightly different behavior, making the change and having it work reliably will be a non-trivial task. |
Yeah, if we were to start shiny over, I would prefer jsonlite over RJSONIO, but it is too late now. |
Ok I understand. |
Ehhh. jsonlite has its own issues that make it not a great fit for Shiny. > jsonlite::toJSON(list(a=NULL))
{"a":{}}
> jsonlite::toJSON(list(b="hi"))
{"b":["hi"]} |
Good point. Cc @jeroenooms > jsonlite::fromJSON(jsonlite::toJSON(list(a=NULL)))
$a
list() |
@jcheng5 @yihui This is intended. The motivation behind this is that the json
The However note that in R, the
That is the motivation behind the current behavior, and that is why the |
Also @jcheng5 if you find any other issues I would love to hear about it obviously. If you are unhappy with the fact that
If you automatically want to unbox all atomic vectors of length 1 as
However because this is very risky when you work with dynamic data (it is actually one of the main reasons I forked RJSONIO), this is not done by default. |
@jeroenooms I understand. For the use case of passing R data to JS, the jsonlite behavior is very sensible. But a lot of what Shiny does is passing R metadata (e.g. the options for a datatable) which has a quite different set of desirable defaults, both for null handling and for unboxing. The
(We had to hack this behavior into RJSONIO for Shiny) |
@jcheng5 The example that you give has nothing to do with
It is because
If you rather want arrays, you can either cast your data frame to a list using
|
@jcheng5 In my experience, it is safer to explicitly use |
Oh, I totally understand the danger and I'm not happy about the ambiguity. But the usability hit of explicit unboxing is just devastating in my opinion (for metadata, not data). RJSONIO just picks a different default, use No easy answers anywhere here--just different tradeoffs. |
In jsonlite I decided to go with the safer default, but provide the same convenience as RJSONIO via the |
Grrrrrrrrrrr. RJSONIO is disappointing me today. > cat(RJSONIO::toJSON(list(a=character()), pretty=TRUE))
{
"a" :
} @jeroenooms Keep in mind we don't have the luxury of controlling both ends of the JSON serialization/deserialization; Shiny has been out in the wild for two years and allows user code in the browser to do whatever it wants with the deserialized representation. So at this point we'd really need to be, if not bug-for-bug compatible with RJSONIO, at least, to use the same representations for all but the most egregious edge cases. |
My apologies to RJSONIO, apparently the bug I just posted is fixed in the latest CRAN release :) |
I do not trust Duncan or Jeroen because of who they are, but I trust Jeroen's tests 👍 Duncan does have a tests directory, but it seems they are "eyeball" tests instead of unit tests, which always makes me nervous since the behavior of functions is not well defined. It is hard to say which behavior is correct/desirable in JSON conversions, but a behavior that is not consistent is certainly not good. |
There are a couple issues related to escaping backslashes in names in RJSONIO, which have been causing me some issues: jsonlite also has one of these: |
@jeroenooms As Joe mentioned, we often don't have control of both ends; in some cases, we want to construct JSON objects that are passed directly to Javascript functions in external libraries. That may mean creating a JSON object like If we were to migrate to jsonlite, I think we can get partway there with
(There may be other issues that I haven't encountered.) An aside: I think that it's conceptually more accurate to say that an empty pairlist is the same as |
The json escaping bug should be fixed now. Several people have requested that However let me try to explain why I think
In practice,
However in JavaScript For example, suppose you encode an object with attributes to json:
And then the JavaScript client does:
All goes well as long as the object has at least one attribute. But if for whatever reason one day the object has zero attributes, R will return
For this reason I felt that |
That makes sense. I think the root of the problem is that there isn't a straightforward bijective mapping between R and JSON data structures, so some compromises are necessary -- which compromises you make depends on your goals. In your case, you want to make it possible to safely index into Another factor to consider is that RJSONIO::fromJSON(RJSONIO::toJSON(list(x=NULL))) # x is NULL
# $x
# NULL
RJSONIO::fromJSON(RJSONIO::toJSON(list(x=list()))) # x is empty list
# $x
# list()
RJSONIO::fromJSON(RJSONIO::toJSON(list(x=list(a=1)[0]))) # x is empty named list
# $x
# named list()
jsonlite::fromJSON(jsonlite::toJSON(list(x=NULL)))
# $x
# list()
jsonlite::fromJSON(jsonlite::toJSON(list(x=list())))
# $x
# list()
jsonlite::fromJSON(jsonlite::toJSON(list(x=list(a=1)[0])))
# $x
# list() |
@jch @jcheng5 @yihui In the latest version of
|
@jeroenooms That's great, thanks! |
jsonlite 0.9.11 is on cran now. I think it should be possible to mimic the most relevant
A simple test case:
In the case of
However when simplification is enabled, |
I think we're getting pretty close to having jsonlite replace toJSON... Here's one more case where they still differ though: d <- list(matrix(1:2, ncol=1))
cat(RJSONIO::toJSON(d))
# [
# [ [ 1 ],
# [ 2 ] ]
# ]
jsonlite::toJSON(d, auto_unbox=T)
# [[1,2]] @jeroenooms Do you know of a way to work around this, while still preserving the |
Just a thought -- could jsonlite::toJSON(d, auto_unbox = function(x) !(is.data.frame(x) || !is.matrix(x))) or something to that effect (I may have mixed things up)? |
Yes that is unfortunate. I fixed this in the devel version, but it will be at least a couple of weeks until the next release. Edit: actually if you really need it I can probably release sooner. If you send me a heads up when you are planning on releasing a new shiny, then I'll push out a version of jsonlite. |
Thanks - we just released a new version of Shiny, so it'll be at least another month before we do another. |
OK cool. I'm also looking at the num to string stuff right now. |
Some more comparisons. This reveals some RJSONIO bugs. :) But I think the digits handling in RJSONIO makes more sense, where compare <- function(x, ...) {
resr <- jsonlite::minify(RJSONIO::toJSON(x, ...))
resj <- jsonlite::minify(jsonlite::toJSON(x, dataframe="columns",
null="null", na="null", auto_unbox=TRUE, ...))
if (!identical(resr, resj)) {
cat(paste0("Results differ.",
"\nRJSONIO: ", resr,
"\njsonlite: ", resj
))
} else {
cat(resj)
}
invisible()
}
compare(list(2))
# [2]
compare(data.frame(x=1:2, y=c("a","b")))
# {"x":[1,2],"y":["a","b"]}
# Differences in displaying digits
compare(
data.frame(x=c(1.2323123231, 5.12345678901234e19), y=c("a","b"))
)
# Results differ.
# RJSONIO: {"x":[1.2323,5.1235e+19],"y":["a","b"]}
# jsonlite: {"x":[1.2323,51234567890123399168],"y":["a","b"]}
# In RJSONIO, digits controls precision; in jsonlite, it controls digits after
# the decimal.
compare(
data.frame(x=c(1.2323123231, 5.12345678901234e19), y=c("a","b")),
digits = 2
)
# Results differ.
# RJSONIO: {"x":[1.2,5.1e+19],"y":["a","b"]}
# jsonlite: {"x":[1.23,51234567890123399168],"y":["a","b"]}
# This is a RJSONIO bug
compare(list(x = matrix(nrow = 0, ncol = 1)))
# Error: parse error: unallowed token at this point in JSON text
# { "x": }
# (right here) ------^
compare(list(matrix(1:2, nrow=1)))
# [[[1,2]]]
compare(list(matrix(1:2, ncol=1)))
# [[[1],[2]]]
compare(list(x = matrix(1:4, nrow=2)))
# {"x":[[1,3],[2,4]]}
# Difference in 1x1 matrix - I think jsonlite is correct
compare(list(x = matrix(1)))
# Results differ.
# RJSONIO: {"x":[1]}
# jsonlite: {"x":[[1]]}
compare(list(x=NULL))
# {"x":null}
compare(list(as.data.frame(t(matrix(1:2, ncol=1)))))
# [{"V1":[1],"V2":[2]}] |
FYI the fix above on on cran now (jsonlite 0.9.13) |
Great, thanks! |
shiny probably switched json libraries (see long and fascinating discussion of R-JSON encoding here: rstudio/shiny#572) so now they're sending scalars as 1-length arrays with annotations which we capture as.. objects.
Hello,
The RJSONIO is known to be incompatible with devtools, cf r-lib/devtools#427
Would it be possible to switch to jsonlite, which is a fork of RJSONIO is ?
Thanks
The text was updated successfully, but these errors were encountered: