Plumber: jsonlite auto_unbox

Created on 12 Sep 2016  路  13Comments  路  Source: rstudio/plumber

I am trying to send back a JSON response like this:

{"status_code":101,"status_message":"Invalid object"}

Here is my code:

return (list(status_code=101, status_message="Invalid object"))

The response I get is:

{"status_code":[101],"status_message":["Invalid object"]}

I'd like to NOT have the brackets (array).

It seems like this is in the call to jsonlite::toJSON

library(jsonlite)
ret <- list(status_code=101, status_message="Invalid object"))
toJSON(ret)
{"status_code":[101],"status_message":["Invalid object"]} 
toJSON(ret, auto_unbox = TRUE)
{"status_code":101,"status_message":"Invalid object"} 

I tried defining a new serializer, but that seems like a hack.

Is there a way around this?

Thanks!

Most helpful comment

FYI I added an unboxing serializer to plumber, so you can now use the @serializer unboxedJSON annotation on any function that you want to use unboxed JSON. This way you won't have to add your own custom serializer.

I'm still debating whether or not we'll swap these out to make this the default in v1.0

All 13 comments

I'm afraid you're on the right track. It would be a serializer that behaves differently from the existing JSON serializer, so I think you'd be looking at a custom serializer, unfortunately. If you find a way to do this such that it might be useful to others I'd welcome a PR. Perhaps this could be a global option that subtly tweaks the existing serializer's behavior.

I'm on the fence about whether or not that would be a desirable feature, though.

Do you have any particular reasons to choose jsonlite to serialize json data?
Switch to rjson is a solution.

> d = list(a='string', b=1)
> rjson::toJSON(d)
[1] "{\"a\":\"string\",\"b\":1}"
> jsonlite::toJSON(d)
{"a":["string"],"b":[1]}

And if you want array result:

> d2 = list(a=list('string'), b=list(1))
> rjson::toJSON(d2)
[1] "{\"a\":[\"string\"],\"b\":[1]}"

I did not have any strong reason for choosing one over the other, but at this point it would be a backwards incompatible change so I'm afraid we're stuck with the current behavior, though I'd welcome a PR to allow you to control this unboxing more efficiently.

I also need to remove the square brackets. Can someone please tell me how to add a custom serializer and change the serializer used by default.
I am not sure how I can change the serializer to use, Would it be changing the source.R file or a line before r$run??

r <- plumb("~/Work/rosi/source.R")
r$run(port=9876)

There may be a better way to do it, but I just forked the repo and changed this line in the serializer-json.R file:

json <- jsonlite::toJSON(val, auto_unbox = T)

Hope it helps.

I figured the way to add custom serializer and it's discussed here.
http://stackoverflow.com/questions/41965032/r-plumber-json-serializer-auto-unbox

Just leaving here so that if anyone else need to find the reference.

FYI I added an unboxing serializer to plumber, so you can now use the @serializer unboxedJSON annotation on any function that you want to use unboxed JSON. This way you won't have to add your own custom serializer.

I'm still debating whether or not we'll swap these out to make this the default in v1.0

Shiny defaults to auto_unbox=TRUE and have had some headaches around special cases in which this has caused them problems. Most notably with single-row data.frames, though jsonlite has since added an exception for that case. But it sounds like they still have to frequently validate their code to make sure that their client (JavaScript, in this case) can properly handle the response of a vector which might someday be length one -- at which point it would no longer be serialized as an array.

I agree that more people want the auto_unboxed behavior, but I feel that auto_unbox can generate surprising behavior, so I'd rather not have it as the default. What I would like to do is make it simpler for people to change the default serializer to the unboxedJSON. Either as a global option in R or just a call to swap out the default.

Is there a final decision on the unboxing? Right now what is the best way to unbox output that is not an array?

Yeah, I think the final decision is that I plan to keep the current behavior

Current workaround: Use the development version and annotation your endpoints with @serializer unboxedJSON

Eventually I'd like to document and make it clear how you could change the default serializer for your whole API so you don't have to add those annotations to each endpoint.

Apologies for asking a stupid question but how do we achieve this?

...and annotation your endpoints with @serialized unboxedJSON

Sorry, typo. It would be

#' @get
#' @serializer unboxedJSON
function(){
  123
}

Closing as this is resolved

Was this page helpful?
0 / 5 - 0 ratings