Can I store RegExp and Function in JSON?

42,958

Solution 1

You have to store the RegExp as a string in the JSON object. You can then construct a RegExp object from the string:

// JSON Object (can be an imported file, of course)
// Store RegExp pattern as a string
// Double backslashes are required to put literal \ characters in the string
var jsonObject = { "regex": "^http:\\/\\/" };

function fun(url) {
    var regexp = new RegExp(jsonObject.regex, 'i');

    var match;

    // You can do either:
    match = url.match(regexp);
    // Or (useful for capturing groups when doing global search):
    match = regexp.exec(url);

    // Logic to process match results
    // ...
    return 'ooga booga boo';
}

As for functions: they should not be represented in JSON or XML anyway. A function may be defined as an object in JS, but its primary purpose is still to encapsulate a sequence of commands, not serve as a wrapper for basic data.

Solution 2

You can't. JSON is for data only, not code. There's no way to transfer functions, unless you can transform it to a string first.

Solution 3

You could do something like this...

Method

JSONEX = {

    stringify: function(obj){
        var jsonified = {}
        // loop through object and write string and type to newly stored data structure
        for(i in obj)
            jsonified[i] = {
                // some voodoo to determine the variable type
                type: Object.prototype.toString.call(obj[i]).split(/\W/)[2],
                value: obj[i].toString()
            }    
        return JSON.stringify(jsonified)
    },

    parse: function(json){
        objectified = {}
        obj = JSON.parse(json)
        // loop through object, and handle parsing of string according to type
        for(i in obj)
            if(obj[i].type == "RegExp"){
                var m = obj[i].value.match(/\/(.*)\/([a-z]+)?/)
                objectified[i] = new RegExp(m[1],m[2]);
            } else if(obj[i].type == "String"){
                objectified[i] = obj[i].value
            } else if(obj[i].type == "Function"){
                // WARNING: this is more or less like using eval
                // All the usual caveats apply - including jailtime
                objectified[i] = new Function("return ("+obj[i].value+")")();
            }
            // ADD MORE TYPE HANDLERS HERE ...

        return objectified

    }
}

Usage

myThing = {
    regex: new RegExp("123","g"),
    text: "good",
    func: function(x){
        return x * x
    }
}

json = JSONEX.stringify(myThing)
// "{"regex":{"type":"RegExp","value":"/123/g"},"text":{"type":"String","value":"good"},"func":{"type":"Function","value":"function (x) {\n    return x * x;\n}"}}"

obj = JSONEX.parse(json)
// native object representing original object

N.B.

Almost a good solution, but does not work with regex (for me anyway)

http://jsonplus.com/

// doing this: jsonPlus.stringify(myThing)
// just stores `regex` as an empty object

Solution 4

TL;DR:

Storing both as objects seems the best to me:

{
    "regexp": {
        "body": "^http:\\/\\/",
        "flags": ""
    },
    "fun": {
        "args": [],
        "body": ""
    }
}

RegExps:

There are already a lot of great answers for storing regexps: store them as raw strings, so that one can use RegExp constructor to create an actual regexp from strings.

My addition would be to keep in mind flags. The OP might not need it, but it is definitely has to be addressed. RegExp constructor takes flags in the form of a string as its second parameter. So the contract for JSON-stored regexp would be:

interface JSONStoredRegExp {
    body: string; // "" (empty string) for empty regexp
    flags: string; // "" (empty string) for zero flags
}

... and, for example, this JSON:

{
    "regexp": {
        "body": "abc",
        "flags": "gi"
    }
}

... would produce this regexp:

RegExp(json.regexp.body, json.regexp.flags);
/abc/gi

Functions:

Unless a given function is pure, it seems weird to me to transfer it via JSON. Also, the code of such an algorithm is likely incompatible with any language, other than JavaScript.

Anyway, if that is still necessary to do, I would recommend the same approach: transfer functions as an object, rather than a string. One can also use Function constructor to create functions from serialized list of arguments and body.

interface JSONStoredFunction {
    args: string[]; // [] (empty array) for zero arguments
    body: string; // "" (empty string) for no-op function
}

Function constructor takes body as its last argument, and each of the parameters has to be passed separately; so this JSON:

{
    "fun": {
        "args": [ "x", "y" ],
        "body": "return x + y;"
    }
}

... will produce this function:

Function(...json.fun.args, json.fun.body);
function anonymous(x, y) { return x + y; }

It might be inconvenient to use ... the spread operator. In that case, using .apply might help:

Function.apply(null, json.fun.args.concat(json.fun.body));

Solution 5

In core JSON, there isn't; the JSON spec only allows for primitive values (string/numbers/boolean/null), and arrays and objects.

Share:
42,958
Huang
Author by

Huang

Updated on November 20, 2021

Comments

  • Huang
    Huang over 2 years

    Given a block like this:

    var foo = {
      "regexp": /^http:\/\//,
      "fun": function() {},
    }
    

    What is a proper way to store it in JSON?