R POST response с использованием Plumber API преобразует двойные 10 знаков после запятой (желаемая точность) в двойные 4 знака после запятой на стороне клиента

#r #post #rounding #precision #plumber

#r #Публикация #округление #точность #plumber

Вопрос:

Платформа: — Экземпляр AWS с 16 ядрами и 128 гигабайтами оперативной памяти. — Redhat Enterprise 7.5. — Сервер R. — RStudio. — Plumber API предоставляет функции R в качестве конечных точек веб-службы. — Клиентская сторона — Excel VBA.

Проблема: — Таблица данных с различными типами столбцов, включая double, целые числа и строковые данные. — Непосредственно перед тем, как функция конечной точки R отправляет ответ (таблицу), и когда я проверяю двойные данные в таблице данных, все записи имеют длину от 6 до 10 знаков после запятой. — Как только таблица поступает в формате JSON на стороне клиента, 99% двойных столбцов округляются до 4 знаков после запятой.

Есть идеи, в чем может быть проблема — почему двойные округляются и где происходит округление и как я могу это предотвратить? — Я пробовал разные настройки заголовка запроса, и это не сработало. — Я попытался отправить затронутые двойные столбцы в виде вектора или списка, но я получаю то же самое «принудительное» округление.

Заранее спасибо

Ответ №1:

Это было не очень хорошо документировано, но оказывается, что это результат использования значений по умолчанию в jsonlite::toJSON сериализаторе ( digits = 4 ). Здесь есть некоторые подробности:

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

Я не вижу, как передать аргумент в это из вашей параметризации, но вот обходной путь:

 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
  

Тогда вы должны быть в состоянии получить ответ, подобный этому:

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

Итак, каким бы ни был результат вашей функции, предварительно сериализуйте его с помощью jsonlite::toJSON(..., digits = NA) и сохраните его непосредственно в теле ответа, затем верните объект response.


Оказывается, есть «правильный» способ сделать это, о котором я узнал, зарегистрировав это как проблему на GitHubhttps://github.com/trestletech/plumber/issues/403 . Однако, похоже, что эта версия еще не включена в CRAN, поэтому вы можете пока использовать исправление, приведенное выше.

В определении вашего API укажите сериализатор следующим образом:

#' @serializer json list(digits = 12)

или, в частности, для json

#' @json(digits = 12)

Комментарии:

1. Большое спасибо Брайану. Но есть ли лучший способ сделать это вместо того, чтобы сериализовать ответ самостоятельно? Например, есть ли настройка или какая-либо функция в Plumber API, которую я могу вызвать или манипулировать, чтобы получить 10 цифр?

2. Большое спасибо Брайану. Это не сработало. Я пробовал оба варианта, но они не сработали (#’ @serializer json list (digits = 12) или #’ @json (digits = 12)). Двойное значение по-прежнему округляется. Может ли это быть проблемой с версией? У меня есть Plumber 0.4.4

3. @Tiger Я думаю, что они были добавлены только в версии разработки 0.5.0, которой еще нет в CRAN.

4. Спасибо, Брайан. Последний вопрос, пока мы продолжаем тему. Иногда, когда я отправляю запрос на конечную точку Plumber, некоторые символы не проходят гладко сериализацию Plumber, и они приводят к тому, что конечная точка не видит тело запроса. Знакомы ли вы с проблемой, и если да, то какой подход был бы наилучшим?

5. Спасибо, Брайан. #’ список json @serializer (цифры = 12) работает в Plumber API 0.4.7

Ответ №2:

Если вы хотите установить это для всех маршрутов, вы также можете использовать следующее (в plumber.R

 #* PI
#* @get /pi
function() {
  pi
}

#* e
#* @get /e
function() {
  exp(1)
}

# Below is "new" and solves this
#* @plumber
function(pr) {
  pr <- pr |>
    # Overwrite the default serializer to return more digits
    pr_set_serializer(serializer_json(digits = 10))
}
  

Который возвращает

 # print 10 digits
options(digits = 10)

# returned JSON has 10 digits
httr::content(httr::GET("http://127.0.0.1:8850/pi"), as = "text")
#> [1] "[3.1415926536]"

# automatically converting to R returns 10 digits
httr::content(httr::GET("http://127.0.0.1:8850/pi"))
#> [[1]]
#> [1] 3.1415926536