我可以在JSON存储RegExp和函数

Can I store RegExp and Function in JSON?

本文关键字:函数 RegExp 存储 JSON 我可以      更新时间:2023-09-26

给定这样的代码块:

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

在JSON中存储它的正确方法是什么?

必须将RegExp作为字符串存储在JSON对象中。然后,您可以从字符串构造一个RegExp对象:

// 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';
}

至于函数:它们无论如何都不应该用JSON或XML表示。函数在JS中可以定义为对象,但其主要目的仍然是封装命令序列,而不是作为基本数据的包装器。

你可以这样做…

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
    }
}
使用

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

注意:

几乎是一个很好的解决方案,但不与regex工作(对我来说)

http://jsonplus.com/

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

br:

将两者都存储为对象对我来说似乎是最好的:

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

regexp:

关于存储regexp已经有很多很好的答案:将它们存储为原始字符串,以便可以使用RegExp构造器从字符串创建实际的regexp。

我的补充是要记住标记。OP可能不需要它,但它肯定必须解决。RegExp构造函数接受字符串形式的标志作为其第二个参数。所以json存储的regexp的契约是:

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

…例如,这个JSON:

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

…将产生以下regexp:

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

功能:

除非给定的函数是纯的,否则通过JSON传输它对我来说似乎很奇怪。此外,这种算法的代码可能与除了JavaScript之外的任何语言都不兼容。

无论如何,如果仍然需要这样做,我将推荐相同的方法:将函数作为对象,而不是字符串。还可以使用Function构造函数从序列化的参数列表和函数体创建函数。

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

Function构造函数以body作为最后一个参数,每个参数必须单独传递;所以这个JSON:

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

…将产生如下函数:

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

使用...作为展开运算符可能不方便。在这种情况下,使用.apply可能会有所帮助:

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

在core JSON中,没有;JSON规范只允许原始值(string/numbers/boolean/null)、数组和对象。

有很好的运动使用https://json5.org/一些新的JSON超集,包含了很多JSON中不合法的东西

{
  // comments
  unquoted: 'and you can quote me on that',
  singleQuotes: 'I can use "double quotes" here',
  lineBreaks: "Look, Mom! '
No ''n's!",
  hexadecimal: 0xdecaf,
  leadingDecimalPoint: .8675309, andTrailing: 8675309.,
  positiveSign: +1,
  trailingComma: 'in objects', andIn: ['arrays',],
  "backwardsCompatible": "with JSON",
}

github: https://github.com/json5/json5

它不是JSON,但它是一种序列化形式:foo.toSource()给出字符串表示:"({regexp:/^http:''/''//, fun:(function () {})})"。使用bar = eval(foo.toSource());将一个带有正则表达式和函数的新对象赋给bar

我不知道它有多受支持。几个网站提到,这是壁虎的专属,尽管他们已经两岁了。我目前只能访问Firefox,所以你可以测试它是否可以在你想要支持的浏览器(可能是IE, Chrome, Safari和Opera)中工作。

我已经使用并推荐雅虎的serialize-javascript npm包。它可以用函数和正则表达式序列化JSON,并处理其他情况。

从他们的文档:

var serialize = require('serialize-javascript');
const serialized = serialize({
    str  : 'string',
    num  : 0,
    obj  : {foo: 'foo'},
    arr  : [1, 2, 3],
    bool : true,
    nil  : null,
    undef: undefined,
    fn: function echo(arg) { return arg; },
    re: /([^'s]+)/g
});

生产

'{"str":"string","num":0,"obj":{"foo":"foo"},"arr":[1,2,3],"bool":true,"nil":null,"fn":function echo(arg) { return arg; },"re":/([^''s]+)/g}'

可与

水化
const obj = JSON.parse(serialized)

这可以通过查看他们的单元测试来验证。

在JSON中保存正则表达式模式是一件痛苦的事情。一个正则表达式字符串,如/^'S+@'S+'.'S+$/(用于检查电子邮件地址格式)需要在JSON中转义到/^''S+@''S+''.''S+$/才能成为有效的JSON。

在javascript中,为了避免必须处理我存储在JSON中的转义和反转义正则表达式模式,我在解析JSON之前使用encodeURIComponent()对正则表达式模式进行编码。因此,如果我想保存一个正则表达式模式,如{"pattern": "/^'S+@'S+'.'S+$/"}在JSON(这将不会验证为JSON),那么编码的模式将被保存在JSON作为{"pattern": "%5E%5CS%2B%40%5CS%2B%5C.%5CS%2B%24"}。这不是理想的,但却是我能找到的避免解析问题的最不痛苦的方法。

为了从JSON中提取正则表达式模式字符串并转换为正则表达式文字值,我在模式上使用decodeURIComponent(),然后使用new RegExp()构造函数创建正则表达式文字。

// patternValue = /^'S+@'S+'.'S+$/
const json = JSON.parse(`{"pattern": "${encodeURIComponent(patternValue)}" }`);  // {"pattern": "%5E%5CS%2B%40%5CS%2B%5C.%5CS%2B%24" }
const dp = decodeURIComponent(json.pattern);
const regExString = new RegExp(dp); 
console.log(regExString);   // /^'S+@'S+'.'S+$/

以上所有答案都是正确的,您可以将这部分代码保存为字符串,然后执行eval()

var sCode = 'var oFoo = {"regexp": new RegExp("/^http:'/'//"), "fun": function() {}}';
eval(sCode);
console.log(oFoo);