4

I have a website that returns a JSON-like data structure like this:

{
    "name":"tom jones",
    "no": 123,
     "storedproc": function(){
                      callbuyer(0123);
                    }
}

I'm getting this data using $.ajax() with dataType "JSON". Unfortunately, my $.ajax() calls the error callback because my data contains a function(). How can I parse this correctly? I really need to store the function in a variable and call it later.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Netorica
  • 18,523
  • 17
  • 73
  • 108

6 Answers6

7

That is simply not legal JSON (as you know given the title of the question) See the offical JSON syntax. The nice thing about real JSON is that one can use JSON.parse which safely wraps an eval call.

While eval could be used, I would suggest revisiting the architecture of your application and find some other way to do what you are trying to do.

In particular, I would have the server return the 0123 only, and let your client keep the logic that lets it know, in certain cases, which functions apply (in the scenario here, the function would be callbuyer).

This should work because you say you want to call the function which is the value of the storedproc later. Since the body of this function contains a call to callbuyer it follows that your client side script knows what callbuyer is. The trick is for your server not to send back arbitrary, unconstrained functions, but rather data that your client can exploit somehow using the knowledge it has about the overall application.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Ray Toal
  • 86,166
  • 18
  • 182
  • 232
  • well my problem here is the function inside the storeproc node is not always the same. – Netorica Jul 19 '12 at 07:58
  • @Ray Toal The basic parser which Doug Crockford provides in his JavaScript: The Good Parts book never resorts to `eval`, though you motivated me to look at the YUI 2.8 JSON parser and it does call `eval` after checking the string is safe. That was a surprise. I'll be looking at libraries more closely in future. – DavidHyogo Jul 19 '12 at 08:19
  • 2
    Yep, you can write them without `eval` and you can write them with `eval`. Crockford uses it here: https://github.com/douglascrockford/JSON-js/blob/master/json2.js – Ray Toal Jul 19 '12 at 08:33
  • Well, that was an eyeopener Ray! – DavidHyogo Jul 19 '12 at 09:23
4

Could you arrange to have the server return JSON like this:

{"name":"tom jones",
  "no": 123,
 "storeprocFn": callbuyer,
 "arg": "0123"};

Then your callback function can call the callbuyer function and pass arg

DavidHyogo
  • 2,838
  • 4
  • 31
  • 48
  • +1 Wow. Simple and elegant. Suggestion: Make `arg` an array of arguments. – Ray Toal Jul 19 '12 at 08:01
  • @Mahan Thanks Mahan. It's worth reading Doug Crockford's JavaScript: The Good Parts to help you understand that eval is evil, the syntax of JSON, and how to parse JSON without eval. – DavidHyogo Jul 19 '12 at 08:12
2

Use eval to interpret the string as a javascript object. You won't be able to use the JSON data type though. I believe what you need to do is use 'text' as the dataType for the $.ajax call. Then do something like:

var data = eval('(' + text + ')');

Should work. Of course, eval is evil. But it would solve your problem. As long as you can guarantee there isn't anything malicious in the text (no unsanitized, user entered data) then you should be ok.

Matt Wolfe
  • 8,924
  • 8
  • 60
  • 77
  • `eval` is well suited for evaluating JSON-like strings into objects alright, but isn't that what jQuery would already do and return an error? – Fabrício Matté Jul 19 '12 at 07:50
  • You could do some kind of pattern matching on the input string, remove any functions, then try to run it through http://api.jquery.com/jQuery.parseJSON/ – Matt Wolfe Jul 19 '12 at 07:59
  • well im sure that the data from the server is always secure. its server generated functions – Netorica Jul 19 '12 at 08:00
1

AFAIK, functions are left out when using JSON.stringify, it's just not meant to be used to clone full objects (props and methods). However, you might be able to pass the function body as a string.
Say you decide on a string format like func=>var foo = 'bar'; return foo;. This should be passed as a regular JSON string, after parsing the object you could then iterate all properties, and convert those strings to functions like so:

for (var prop in parsedObj)
{
    if (parsedObj.hasOwnProperty(prop) && parsedObj[prop].match(/^func\=\>/))
    {
        parsedObj[prop] = new Function(parsedObj[prop].replace('func=>',''));
    }
}

Though, seriously, I'd say you might want to rethink your approach, this is not what JSON is for. It's unsafe, all JSON strings are eval'ed, after having made sure they contain no harmful code. This approach is creating a loophole/vulnerability that the JSON people worked hard for to seal off.

Elias Van Ootegem
  • 74,482
  • 9
  • 111
  • 149
  • Wouldn't it be better to have a bunch of functions defined on the client side and the server side then just passes the reference to the function and the client side takes it from there? I feel passing the body of a function as a string from the server side could be very brittle and error-prone. – DavidHyogo Jul 19 '12 at 08:14
  • It is, that's why I suggested the OP to rethink his approach a bit. Your solution looks nice though, apart from 1 slight drawback (I'm nitpicking though): the references will be strings, so you'll have to iterate the object and change the `storedproc` property to `window[parsedObj.storedproc]`, to create the reference, but what if your function is not a global function? more and more people use `(function(){'use strict'; var foo = function(){return 'bar';};})();`, in which case `window.foo` is undefined, so `window[parsedObj.storedproc]` won't work – Elias Van Ootegem Jul 19 '12 at 08:23
  • Yes, good point. As my references to Doug Crockford's book will show, I avoid global variables wherever possible, so I might have an object in my App's namespace with a collection of functions: `APP_NAME.storedProcs.procA`, `APP_NAME.storedProcs.procB` or something like that. – DavidHyogo Jul 19 '12 at 09:18
  • :) it hadn't occurred to me yet to use a namespace object. In that case: you're suggestion is elegant, and pretty sturdy... +1 – Elias Van Ootegem Jul 19 '12 at 09:22
0

For your example will this work: 'user.storeproc = function() { callbuyer( user.no);};'

The Var 'user' is the object of the parsed json.

Ps: maybe you have to format user.no, from 123 to 0123

Ties
  • 1,776
  • 2
  • 12
  • 17
0

Following JSON extension, "JFON", does transport of functions and array-properties.
JFON uses eval and is intended for case if:

1) your data is from trusted source ( like not-derived from user input or is a code from your own server), and
2) you know there are no undesired side effects with context of "eval"
(it is a context of eval in function "fromJFON", line 127 )
3) it is costly to refactor your app to use "functionless" JSON;
4) JFON is one-day work, so may be needs more testing;

The idea: use selected property name to escape functions and arrays like
in strings when selected character "\" is used to pass \n and \ for itself.

In JFON, name "wrap" is selected to pass functions and itself: "wrap" : { "fun" : ... and "wrap" : { "esc" : ...

demo: http://landkey.org/Sandbox/z/spaceen86/js/btb/tests/jfon.htm

code ( use commit 0.0.86 ):
https://github.com/lancelab/spaceen/blob/master/js/btb/JFON.js
test: github.com/lancelab/spaceen/blob/master/js/btb/tests/jfon.htm

Here is another, "JWON" extension: JSON-comments, here-documents, monkey-patching of JSONs:
github.com/lancelab/Boardspirator/blob/master/diary/play/tp/jwon.js

Bitlab
  • 43
  • 6