4

I have been experimenting with Plumber in R recently, and am having success when I pass the following data using a POST request;

{"Gender": "F", "State": "AZ"}

This allows me to write a function like the following to return the data.

#* @post /score
    score <- function(Gender, State){
      data <- list(
        Gender = as.factor(Gender)
        , State = as.factor(State))

      return(data)
      }

However, when I try to POST an array of JSON objects, I can't seem to access the data through the function

[{"Gender":"F","State":"AZ"},{"Gender":"F","State":"NY"},{"Gender":"M","State":"DC"}]

I get the following error

{
    "error": [
        "500 - Internal server error"
    ],
    "message": [
        "Error in is.factor(x): argument \"Gender\" is missing, with no default\n"
    ]
}

Does anyone have an idea of how Plumber parses JSON? I'm not sure how to access and assign the fields to vectors to score the data.

Thanks in advance

MattW
  • 41
  • 2
  • 4
    I don't think this is going to be well handled today. I created an issue here. https://github.com/trestletech/plumber/issues/236. One workaround would be to name the array inside an object, like `{arr: ["Gender": ...` then you could access it at the `arr` parameter – Jeff Allen Feb 07 '18 at 18:05
  • 2
    Thanks. Naming the array solved the issue for now. – MattW Feb 14 '18 at 15:08
  • complete wild guess here - what happens if you define your function as `score <- function(...)` then unpack the dots inside accordingly – davidski Mar 15 '18 at 09:09

1 Answers1

0

I see two possible solutions here. The first would be a command line based approach which I assume you were attempting. I tested this on a Windows OS and used column based data.frame encoding which I prefer due to shorter JSON string lengths. Make sure to escape quotation marks correctly to avoid 'argument "..." is missing, with no default' errors:

curl -H "Content-Type: application/json" --data "{\"Gender\":[\"F\",\"F\",\"M\"],\"State\":[\"AZ\",\"NY\",\"DC\"]}" http://localhost:8000/score
# [["F","F","M"],["AZ","NY","DC"]]

The second approach is R native and has the advantage of having everything in one place:

library(jsonlite)
library(httr)

## sample data
lst = list(
  Gender = c("F", "F", "M")
  , State = c("AZ", "NY", "DC")
)

## jsonify
jsn = lapply(
  lst
  , toJSON
)

## query
request = POST(
  url = "http://localhost:8000/score?"
  , query = jsn # values must be length 1
)

response = content(
  request
  , as = "text"
  , encoding = "UTF-8"
)

fromJSON(
  response
)
#      [,1]                    
# [1,] "[\"F\",\"F\",\"M\"]"   
# [2,] "[\"AZ\",\"NY\",\"DC\"]"

Be aware that httr::POST() expects a list of length-1 values as query input, so the array data should be jsonified beforehand. If you want to avoid the additional package imports altogether, some system(), sprintf(), etc. magic should do the trick.

Finally, here is my plumber endpoint (living in R/plumber.R and condensed a little bit):

#* @post /score
score = function(Gender, State){
  lapply(
    list(Gender, State)
    , as.factor
  )
}

and code to fire up the API:

pr = plumber::plumb("R/plumber.R")
pr$run(port = 8000)
fdetsch
  • 5,239
  • 3
  • 30
  • 58