Search code examples
rpostroundingprecisionplumber

R POST response using Plumber API converts 10-decimal-places double (desired precision) to 4-decimal-places double at client side


Platform: - AWS Instance with 16 cores and 128 GIG RAM. - Redhat Enterprise 7.5. - R. - RStudio Server. - Plumber API exposes R functions as Web Service endpoints. - Client side is Excel VBA.

Problem: - Data table with different types of columns including double, integers, and strings data. - Right before R endpoint function sends the response (the table) and when I check the double data in the data table, all the entries are between 6 and 10 decimal-place long. - As soon as the table arrives in JSON format at the client side, 99% of the double columns are rounded to 4 decimal-place long.

Any idea what could be the problem - why do the doubles get rounded and where does the rounding take place and how can I prevent that? - I tried different request header settings and it did not work. - I tried to send the impacted double column/s as a vector/s or list/s but I get the same "enforced" rounding.

Thanks in advance


Solution

  • This wasn't terribly well-documented, but it turns out it's a result of using the defaults in the jsonlite::toJSON serializer (digits = 4). There's some details here:

    https://www.rplumber.io/articles/rendering-output.html

    I don't see how to pass an argument into that from your parameterization, but here's a workaround:

    library(plumber)
    
    #* @apiTitle A Test API
    
    #* Run a simple function
    #* @get /
    
    function(req, res) {
      x <- rnorm(1)
      res$body <- jsonlite::toJSON(x, digits = NA)
      res
    }
    
    
    # plumb("plumber_1.R")$run(port = 5762)
    # Save this file as e.g. "plumber_1.R" and run the commented line
    

    Then you should be able to get a response like this:

    library(httr)
    y <- GET("http://127.0.0.1:5762/")
    content(y, as = "text")
    
    [1] "[-0.982448323838634]"
    

    So whatever the result of your function is, pre-serialize it using jsonlite::toJSON(..., digits = NA), and store it directly in the body of the response, then return the response object.


    It turns out there is a "correct" way of doing this, which I found out by filing this as a GitHub issue https://github.com/trestletech/plumber/issues/403. However, it looks like this version isn't on CRAN yet, so you can use the fix above in the meantime.

    In your API definition, specify the serializer like this:

    #' @serializer json list(digits = 12)

    or for json specificially

    #' @json(digits = 12)