如何对JavaScript对象进行序列化和反序列化?

48

我需要将JavaScript对象序列化并反序列化以将其存储在数据库中。

请注意,这些对象包含函数,因此我无法将它们存储为JSON,所以我不能使用json2.js。

在JavaScript中,[反]序列化JavaScript对象的最新技术是什么?


3
如果一个 JavaScript 对象包含函数(即方法),那么就无法将其编码为 JSON。JSON 只处理纯数据的 JS 对象(哈希、数组和基本类型)。 - Stewart Johnson
具体来说:我需要存储来自Google Maps Javascript API的DirectionsResult对象。 DirectionsResult包含LatLng 对象,它们具有JSON无法处理的函数:http://code.google.com/apis/maps/documentation/javascript/reference.html#LatLng - Stewart Johnson
我们不能将函数对象作为字符串存储在 JSON 中吗? - Viku
我想序列化包含字符串的对象,以便通过HTML传递它们,允许所有字符。JSON禁止控制字符。我不需要序列化函数。有没有适用于此的简短函数? - David Spector
6个回答

34

一般来说,(在浏览器中)没有办法将带有函数的对象序列化:每个函数都有一个对其外部作用域的引用,而当您对其进行反序列化时,该作用域将不存在,因此对该作用域的序列化引用将无效。

我会使用内置的(或json2.js)JSON.stringifyJSON.parse函数与replacerreviver参数。下面是示例代码:

JSON.stringify(yourObject, function(name, value) {
    if (value instanceof LatLng) { // Could also check the name if you want
        return 'LatLng(' + value.lat() + ',' + value.lng() + ')';
    }
    else if (...) {
        // Some other type that needs custom serialization
    }
    else {
        return value;
    }
});

JSON.parse(jsonString, function(name, value) {
    if (/^LatLng\(/.test(value)) { // Checking the name would be safer
        var match = /LatLng\(([^,]+),([^,]+)\)/.exec(value);
        return new LatLng(match[1], match[2]);
    }
    else if (...) {
        ...
    }
    else {
        return value;
    }
});
你可以在自定义类型中使用任何你想要的序列化格式。"LatLng(latitude,longitude)" 格式只是其中一种方式。你甚至可以返回一个 JavaScript 对象,可以本地序列化为 JSON。

有趣。我读了json2.js文档(“在JSON中没有表示形式的值(例如函数和未定义)被排除在外。”),并认为替换器/恢复器函数不会调用函数。这是您实际编写和使用的代码吗? - Stewart Johnson
@Stewart:这个想法是避免在反序列化时需要对函数进行序列化,而是通过重新构建对象来实现。LatLng 构造函数会为您添加回函数。替换器可以用于函数,所以如果您真的想要,可以尝试将函数转换为字符串。但请注意,它并不适用于每种情况。 - Matthew Crumley
在字符串中无法处理某些字符,例如换行符。 - David Spector

9

您不希望序列化诸如函数之类的逻辑。

如果您将来必须更新逻辑/ js函数,您不(总是)希望较旧的逻辑随数据重新加载。请注意。


7

我之前遇到过这个,但我不确定这个项目的成熟度。你个人使用过吗? - Stewart Johnson
4
看起来行不通:Gserializer中的TODO提示说:“实现将参数传递给序列化函数(当前仅支持无参函数)”,而我需要序列化的对象包含带有参数的函数(如LatLng对象上的toUrlValue函数:http://code.google.com/apis/maps/documentation/javascript/reference.html#LatLng)。不管怎么样,还是谢谢! - Stewart Johnson
抱歉,我希望他们能完成这个项目。 - Haim Evgi
他已经一年没有碰这个项目了。太糟糕了。我还需要带参数的函数。 - huyz

2
在 Node.js 上,还有一个名为 JASON 的包(https://github.com/xk/JASON)。
以下是示例:
var JASON = require("JASON");

str = JASON.stringify(obj);
obj = JASON.parse(str);

通过以下方式安装软件包:npm install JASON


1
如果您使用ES6版本的Node,可以查看我编写的一个小包JSOFF。它是JavaScript对象函数格式;JSON的替代品,可以处理函数。
它非常小巧简单,所以Babeljs或Browserify可能会对您有帮助。
安装方式:通过npm install jsoffyarn add jsoff进行安装。
下面是如何创建带有函数的对象的示例:
const JSOFF  = require('jsoff');

var obj = {
  abc: 123,
  def: function (a,b) { return a * 2 + b * 3; },
  ghi: a => { return a * 2 },
  jkl: (a,b) => { return ((d,e) => { return a*d + b*e })(2,4) }
};

var str = JSOFF.stringify(obj);
// str is now:  
// '{"abc":123,"def":"function (a,b) { return a * 2 + b * 3; }","ghi":"a => { return a * 2 }","jkl":"(a,b) => { return ((d,e) => { return a*d + b*e })(2,4) }"}');
});

var clone = JSOFF.parse(str);
clone.def(10,5)   // 35
clone.ghi(5)      // 10
clone.jkl(10,20)  // 100

0

出于安全原因,我不会序列化JS函数。通过公共API,各种恶意内容都可能被发送到数据库中。至于反序列化,我有一种不同的方法。我将客户端定义的模型对象与来自JSON的数据混合在一起。我有一个小工具可以做到这一点,在GitHub上查看khayll/jsonmix

JsonMix提供了一种从JSON反序列化为JavaScript对象的方式,包括函数。

它看起来像这样:

//model definition (just an example)

var LatLng = function() {}
LatLng.prototype.getMapTypeId = function() {
   return this.mapTypeId;
}

//deserializing done like this
var result = JSMix(jsonString).withObject(LatLng.prototype, "latLngs").build();

//all items in the latLngs collection have the functions coming from the model
console.log(result.latLngs[5].getMapTypeId());

2
你能解释一下为什么在任何情况下对JavaScript函数进行序列化可能存在安全风险吗? - Codebling
1
如果您有一个公共API,可以接受包含功能的序列化JavaScript对象,并在Web应用程序中使用它,则可能会向您的服务器发送恶意代码。假设您还在Web应用程序的某个地方读取这些对象并使用它们。现在,如果有人覆盖了该对象上的toString()函数,并且您碰巧在代码中使用它,那么该人就可以更改Web应用程序的预期功能。因此,尽管您从安全URL下载了它,但功能不再可信。 - fishgen
1
当然。但是服务器的接受以及随后的反序列化和代码使用才是风险所在。序列化本身并不危险。 - Codebling
@fishgen Code Bling说得没错。而且,没有必要假设他的序列化用例或目的与你相同。他的目的可能是为了符合法规而进行档案保管;也可能是为了研究目的;或者其他任何目的。 - Sir Robert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接