albertosantini / node-rio

Integration with Rserve, a TCP/IP server for R framework
https://github.com/albertosantini/node-conpa
MIT License
176 stars 35 forks source link

String with single quote not handled correctly in rio. #36

Closed yashdosi closed 7 years ago

yashdosi commented 7 years ago

Cannot pass strings with single quotes in them to R. Here are the code snippets for an example to reproduce the bug.

Nodejs code -

var domain = require('domain');
var path = 'path/to/ex.R';
var inputs = "Hello 'world!";
var rio_callback = function(err, response) {
    console.log(err);
    console.log('asdasd');
    console.log(response);
};

var options = {
    data: inputs,
    entryPoint: 'run',
    callback: rio_callback
};
rio.sourceAndEval(path, options);

R code -

run <- function(params) {
    print('qwkelj')
    print(params)
    return('qweewrwer')
}
albertosantini commented 7 years ago

Thanks for feedback.

It seems you are using an old version of rio and the example you provided contains a few wrong assumptions. Please, check API in README and give a look at the examples in examples folder.

When you use an R script, you need to pass a JSON object as parameter. Then, inside the R script, you can use RJSONIO or jsonlite package for deserializing the JSON object, calling a method, serializing the response and returning the response to javascript.

I refactored the example.

ex11.js

"use strict";

var path = require("path"),
    rio = require("rio");

function displayResponse(err, res) {

    if (!err) {
        res = JSON.parse(res);
        console.log(res);
    } else {
        console.log("Rserve call failed");
    }
}

rio.e({
    filename: path.join(__dirname, "ex11.R"),
    entrypoint: "run",
    data: {
        "hello": 'Hello "world"!'
    },
    callback: displayResponse
});

ex11.R

require(RJSONIO)

run <- function(jsonObj) {
    o = fromJSON(jsonObj)

    toJSON(o)
}

The example displays correctly { hello: 'Hello "world"!' }, when double quotes are inside the string.

Now coming back to the original issue, single quote in a string, you may notice there is problem even if you pass the string with single quote escaped: "hello": 'Hello \'world\'!'.

If you start an Rserve debug instance, you can see the string passed it is the following one: run('{"hello":"Hello \\'world\\'!"}') instead of run('{"hello":"Hello \'world\'!"}').

Indeed the first one raises Error: unexpected symbol in "run('{"hello":"Hello 'world". The second one [1] "{\n \"hello\": \"Hello 'world'!\" \n}".

No idea why Rserve escapes againg the backslash there.

Investigating. In the meantime use double quotes as in the example above.

albertosantini commented 7 years ago

I added an example 11 in examples folder, showing how to deal with single quotes. If you have a mix of quotes in data, the approach is similar.

Basically you need to escape/unescape the string or using accordingly the backslash.

You can try in R console these lines:

> as.character("Hello 'World'")
[1] "Hello 'World'"
> as.character('Hello \'World\'')
[1] "Hello 'World'"

> '"Hello 'world'!"'
Error: unexpected symbol in "'"Hello 'world"
> ""Hello 'world'!""
Error: unexpected symbol in """Hello"

And so on. You can handle in R the strings above adding backslahes or using regex expressions mangling the quotes.

Also the object passed to the entrypoint of R script is stringified and enclosed between single quotes: https://github.com/albertosantini/node-rio/blob/master/lib/evaluate.js#L33

As you can see there is not an agnostic (and simple) approach to pass a string, including a mix of single and double quotes, to the R script. So rio lets the user the responsibility to deal with data.

I hope it makes sense and that helps in your use case.