将json数据转换为附加方法的对象的最简单方法?

将包含对象数据的json转换为附加方法的实际对象的最快捷,最简单的方法是什么?

举例来说,我获得了一个带有一系列水果对象的水果碗的数据,这些水果对象又包含一系列种子,因此:

{"fruitbowl": [{ "name": "apple", "color": "red", "seeds": [] },{ "name": "orange", "color": "orange", "seeds": [ {"size":"small","density":"hard"}, {"size":"small","density":"soft"} ]} } 

这一切都很好,但在客户端我们用这种水果做的东西,比如吃它和种植树木……

  var fruitbowl = [] function Fruit(name, color, seeds){ this.name = name this.color = color this.seeds = seeds this.eat = function(){ // munch munch } } function Seed(size, density){ this.size = size this.density = density this.plant = function(){ // grow grow } } 

我的ajax的成功例程目前正在循环整个事物并依次构造每个对象并且它还没有处理种子,因为在我循环遍历种子构造函数之前我正在思考

有没有更好的方法?

  success: function(data){ fruitbowl.length = 0 $.each(data.fruitbowl, function(i, f){ fruitbowl.push(new Fruit(f.name, f.color, f.seeds)) }) 

我没有探索如何循环遍历对象并附加所有方法。 那会有用吗?

是的,它会起作用,但这是不可取的。 除了看起来有点hacky IMO之外,你还要将方法附加到你的水果和种子的每个实例上,你应该使用原型链。 如果您将来要使用instanceof ,这种方法无论如何都行不通。

你目前正在做的是最好的解决方案; 你将能够使用instanceof

如果您喜欢冒险,可以使用JSONP而不是AJAX,JSONP响应类似于:

 buildFruitbowl([new Fruit("orange", "blue", [new Seed("small", "hard"), new Seed("big", "soft")]), new Fruit("banana", "yellow", [new Seed("small", "hard"), new Seed("big", "soft")])]); 

这将节省您必须完成所有对象循环,并且您将获得您想要的水果和种子(以及支持instanceof ); 但是我仍然坚持你已经在做的事情。

最好看看你的香蕉种植。

将数据传递给对象构造函数,然后使用jquery的“extend”来组合数据和方法:

  function Fruit(data){ $.extend(this, data) this.eat = function(){ // munch munch } } ... $.each(data.fruitbowl, function(i, f){ fruitbowl.push(new Fruit(f)) }) 

你还有循环参与; 并且必须为嵌套对象(如种子)手动编写循环,但仍然是一种非常简单的方法来解决问题。

您可以修改JSON结构以存储类型信息。 如果你有很多对象来回序列化和反序列化,这将节省为每个对象编写自定义代码的时间。

另请注意,这会修改JSON结构并为每个自定义对象添加__type__属性。 我认为这比保留单独的配置文件更简洁。 所以不用多说,这就是它基本上如何工作:

 var fruitBowl = {..}; fruitBowl[0].eat(); fruitBowl[1].seeds[0].plant(); 

调用序列化对象以获取JSON表示

 var json = fruitBowl.serialize(); 

对JSON编码的字符串调用反序列化以重建对象

 var resurrected = json.deserialize(); 

现在您可以访问对象的属性和调用方法:

 resurrected[0].eat(); resurrected[1].seeds[0].plant(); 

它适用于任何级别的深层嵌套对象,尽管它现在可能有点小问题。 此外,它很可能不是跨浏览器(仅在Chrome上测试)。 由于反序列化器不熟悉对象的构造函数,因此它基本上创建每个自定义对象而不传递任何参数。 我在http://jsfiddle.net/kSATj/1/上设置了一个关于jsfiddle的工作演示。

必须修改构造函数以考虑可以创建对象的两种方式

  1. 直接在Javascript中
  2. 从JSON重建

所有构造函数都需要从两端容纳创建,因此需要为每个属性分配一个默认的回退值,而不会传递任何内容。

 function SomeObject(a, b) { this.a = a || false; // defaultValue can be anything this.b = b || null; // defaultValue can be anything } // one type of initialization that you can use in your code var o = new SomeObject("hello", "world"); // another type of initialization used by the deserializer var o = new SomeObject();; oa = "hello"; ob = "world"; 

作为参考,修改后的JSON如下所示:

 {"fruitbowl": [ { "__type__": "Fruit", "name": "apple", "color": "red", "seeds": [] }, { "__type__": "Fruit", "name": "orange", "color": "orange", "seeds": [ { "__type__": "Seed", "size": "small", "density": "hard" }, { "__type__": "Seed", "size": "small", "density": "soft" } ] } ] } 

这只是一个帮助函数来识别简单类型:

 function isNative(object) { if(object == null) { return true; } var natives = [Boolean, Date, Number, String, Object, Function]; return natives.indexOf(object.constructor) !== -1; } 

将对象序列化为JSON(保留类型信息):

 Object.prototype.serialize = function() { var injectTypes = function(object) { if(!isNative(object)) { object.__type__ = object.constructor.name; } for(key in object) { var property = object[key]; if(object.hasOwnProperty(key) && !isNative(property)) { injectTypes(property); } } }; var removeTypes = function(object) { if(object.__type) { delete object.__type__; } for(key in object) { var property = object[key]; if(object.hasOwnProperty(key) && !isNative(property)) { removeTypes(property); } } } injectTypes(this); var json = JSON.stringify(this); removeTypes(this); return json; }; 

反序列化(重建自定义对象):

 String.prototype.deserialize = function() { var rawObject = JSON.parse(this.toString()); var reconstruct = function(object) { var reconstructed = {}; if(object.__type__) { reconstructed = new window[object.__type__](); delete object.__type__; } else if(isNative(object)) { return object; } for(key in object) { var property = object[key]; if(object.hasOwnProperty(key)) { reconstructed[key] = reconstruct(property); } } return reconstructed; } return reconstruct(rawObject); }; 

使用ES5 Object.create

只需静态定义对象,然后使用Object.create扩展它们。

它就像Object.create(Bowl, transform(data));一样简单Object.create(Bowl, transform(data));

 // declare 3 Objects to use as prototypes for your data var Fruit = { eat: function() { } } var Seed = { plant: function() { } } var Bowl = {}; // data object var data = { ... }; // Transform JSON to a valid defineProperties hash. Object.create(Bowl, transform(data)); 

您需要定义转换函数,更重要的是告诉它嵌套数据数组的对象类型。

 // hash map of property names of arrays to the Object they should prototype from. var collectionClassHash = { fruitbowl: Fruit, seeds: Seed } var transform = function(obj) { // return value var ret = {}; // for each key Object.keys(obj).forEach(function(key) { // value of key var temp = obj[key]; // if array if (Array.isArray(temp) { // override value with an array of the correct objects temp = obj[key].map(function(val) { // recurse for nested objects return Object.create(collectionClassHash[key], transform(val)); }); } // define getter/setter for value ret[key] = { get: function() { return temp; }, set: function(v) { temp = v; } } }); return ret; } 

使用D Crockford的“json2”库,您可以为解析过程提供“reviver”function。 reviver函数传递每个键和每个值,并应返回要在解析结果中使用的实际有效值。

“stringify”方法中有相应的可选参数。

这实际上花了我一段时间来弄清楚,我真的很惊讶没有更多的页面。

正如@Pointy指出的那样,JSON有一个reviver函数,可用于替换内联的解析结果,从而避免第二次遍历树。 JSON页面记录了reviver(在我看来有点弱) – http://json.org/js.html

Reviver是ECMA 5的一部分,在Firefox,WebKit(Opera / Chrome)和JSON2.js中受支持。

这是一个基于JSON doc的代码示例。 您可以看到我们正在Dog上设置type属性,然后使用识别该类型属性的reviver函数。

 function Dog(args) { this.name = args.name; this.bark = function() { return "bark, bark, my name is " + this.name; }; this.toJSON = function() { return { name: this.name, type: 'Dog' // this.constructor.name will work in certain browsers/cases } } }; var d = new Dog({name:'geti'}); var dAsJson = JSON.stringify(d); var dFromJson = JSON.parse(dAsJson, function (key, value) { var type; if (value && typeof value === 'object') { type = value.type; if (typeof type === 'string' && typeof window[type] === 'function') { return new (window[type])(value); } } return value; } ); 

我对他们的例子有几个担忧。 首先,它取决于构造函数是全局的(在窗口上)。 第二个是安全问题,因为流氓JSON可以让我们通过向其JSON添加类型属性来调用任何构造函数。

我已经选择了一个明确的类型列表及其构造函数。 这样可以确保只调用我知道安全的构造函数,并且如果我喜欢的话,还允许我使用自定义类型映射方法(而不是依赖于构造函数名称并且它在全局空间中)。 我还validation了JSON对象有一个类型(有些可能没有,它们将被正常处理)。

 var jsonReviverTypes = { Dog: Dog }; var dAsJsonB = JSON.stringify(d); var dFromJsonB = JSON.parse(dAsJsonB, function (key, value) { var type; if (value && typeof value === 'object' && value.type) { type = value.type; if (typeof type === 'string' && jsonReviverTypes[type]) { return new (jsonReviverTypes[type])(value); } } return value; }); 

注意,FF 3.6在JSON.replacer方法中有一个错误,因为@Sky指出并在此处记录 – http://skysanders.net/subtext/archive/2010/02/24/confirmed-bug-in-firefox-3.6 -native-json-implementation.aspx 。 对于上面的解决方案,我通过在对象上使用toJSON而不是使用replacer来解决这个问题。

约翰,

希望在这里筹码还为时不晚。 我上周遇到了一个非常类似的问题,用下面的js解决了它(它也很容易转换成jquery。)。

这是基本用法:

  $(document).ready(function() { var bowl = { "fruitbowl": [{ "name": "apple", "color": "red", "seeds": [] }, { "name": "orange", "color": "orange", "seeds": [ { "size": "small", "density": "hard" }, { "size": "small", "density": "soft"}] } ] }; var serialized = jsonToObject.serialize(bowl); var deserialized = jsonToObject.deserialize(serialized); // basic tests on serialize/deserializing... alert(deserialized.fruitbowl[0].name); alert(deserialized.fruitbowl[1].seeds[0].density); }); 

这是jsonToObject.js文件:

 jsonToObject = { deserialize: function(_obj) { if (typeof (JSON) === 'object' && typeof (JSON.parse) === 'function') { // native JSON parsing is available. //return JSON.parse(_obj); } // otherwise, try non-native methods var jsonValue = new Function("return " + _obj)(); if (!jsonValue instanceof Object) { jsonValue = eval("(" + _obj + ")"); } return jsonValue; }, serialize: function(_obj) { // Let Gecko browsers do this the easy way - not working if (_obj != undefined && typeof _obj.toSource !== 'undefined' && typeof _obj.callee === 'undefined') { return _obj.toSource(); } // Other browsers must do it the hard way switch (typeof _obj) { // numbers, booleans, and functions are trivial: // just return the object itself since its default .toString() // gives us exactly what we want case 'number': case 'boolean': case 'function': return _obj; break; // for JSON format, strings need to be wrapped in quotes case 'string': return '"' + _obj.replace(/"/mg, "'") + '"'; break; case 'object': var str; if (_obj.constructor === Array || typeof _obj.callee !== 'undefined') { str = '['; var i, len = _obj.length; for (i = 0; i < len - 1; i++) { str += this.serialize(_obj[i]) + ','; } str += this.serialize(_obj[i]) + ']'; } else { str = '{'; var key; for (key in _obj) { str += key + ':' + this.serialize(_obj[key]) + ','; } str = str.replace(/\,$/, '') + '}'; } return str; break; default: return '""'; break; } } } 

希望这可以帮助...

吉姆

[编辑] - 你当然也可以给这两个函数提供原型签名,与上面的优秀例子保持一致,即..

String.prototype.deserialize = function(){...} Object.prototype.serialize = function(){...}