如何从JSON恢复原始对象/类型?

时间:2012-12-25 02:17:31

标签: javascript json

使用eval或JSON.parse可以轻松地将JSON加载到javascript中的对象中。

但是如果你有一个像函数一样合适的“类”,那你如何将JSON数据输入呢?

E.g。

function Person(name) {
  this.name=name;
  this.address = new Array();
  this.friendList;

  this.promote = function(){
     // do some complex stuff
  }
  this.addAddress = function(address) {
    this.address.push(address)
  }
}

var aPersonJSON = '{\"name\":\"Bob\",\"address\":[{\"street\":\"good st\",\"postcode\":\"ADSF\"}]}'

var aPerson = eval( "(" + aPersonJSON + ")" ); // or JSON.parse
//alert (aPerson.name);    // Bob
var someAddress = {street:"bad st",postcode:"HELL"};
//alert (someAddress.street); // bad st
aPerson.addAddress(someAddress); // fail!

关键是我需要能够从JSON创建适当的Person实例,但我能得到的只是一个愚蠢的对象。我想知道它是否有可能用原型做一些事情?

我不想解析JSON的每一行,并将每个变量分配给相应的函数属性,这太难了。我所拥有的实际JSON和函数比上面的例子复杂得多。

我假设可以将函数方法JSONify转换为JSON字符串,但是因为我需要保持结果数据尽可能小这不是一个选项 - 我只想存储和加载数据,而不是javascript代码对于方法。

如果我可以提供帮助,我也不想将JSON加载的数据作为子对象(但可能是唯一的方法),例如,

function Person(name) {
  this.data = {};
  this.data.name=name;
}

var newPerson = new Person("");
newPerson.data = eval( "(" + aPersonJSON + ")" );
alert (newPerson.data.name); // Bob

有什么想法吗?

6 个答案:

答案 0 :(得分:21)

您需要使用reviver功能:

// Registry of types
var Types = {};

function MyClass(foo, bar) {
  this._foo = foo;
  this._bar = bar;
}
Types.MyClass = MyClass;

MyClass.prototype.getFoo = function() {
  return this._foo;
}

// Method which will provide a JSON.stringifiable object
MyClass.prototype.toJSON = function() {
  return {
    __type: 'MyClass',
    foo: this._foo,
    bar: this._bar
  };
};

// Method that can deserialize JSON into an instance
MyClass.revive = function(data) {
  // TODO: do basic validation
  return new MyClass(data.foo, data.bar);
};

var instance = new MyClass('blah', 'blah');

// JSON obtained by stringifying an instance
var json = JSON.stringify(instance); // "{"__type":"MyClass","foo":"blah","bar":"blah"}";

var obj = JSON.parse(json, function(key, value) {
  return key === '' && value.hasOwnProperty('__type')
    ? Types[value.__type].revive(value)
    : this[key];
});

obj.getFoo(); // blah

真的没有别的办法......

答案 1 :(得分:7)

许多框架提供了一个“扩展”功能,可以将字段从一个对象复制到另一个对象。你可以将它与JSON.parse结合起来做你想要的。

newPerson = new Person();
_.extend(newPerson, JSON.parse(aPersonJSON));

如果您不想包含下划线之类的内容,您可以随时复制扩展功能或自行编写。

Coffeescript的例子,因为我很无聊:

JSONExtend = (obj, json) ->
  obj[field] = value for own field, value of JSON.parse json
  return obj

class Person
  toString: -> "Hi I'm #{@name} and I'm #{@age} years old."


dude = JSONExtend new Person, '{"name":"bob", "age":27}'
console.log dude.toString()

答案 2 :(得分:4)

派对有点晚了,但这可能对某人有所帮助。 这就是我解决它的方式,ES6语法:

class Page 
{
   constructor() {
      this.__className = "Page";
   }

   __initialize() {
       // Do whatever initialization you need here.
       // We'll use this as a makeshift constructor.
       // This method is NOT required, though
   }
}

class PageSection
{
   constructor() {
      this.__className = "PageSection";
   }
}

class ObjectRebuilder
{
    // We need this so we can instantiate objects from class name strings
    static classList() {
        return {
            Page: Page,
            PageSection: PageSection
        }
    }

    // Checks if passed variable is object.
    // Returns true for arrays as well, as intended
    static isObject(varOrObj) {
        return varOrObj !== null && typeof varOrObj === 'object';
    }

    static restoreObject(obj) {
        let newObj = obj;

        // At this point we have regular javascript object
        // which we got from JSON.parse. First, check if it
        // has "__className" property which we defined in the
        // constructor of each class
        if (obj.hasOwnProperty("__className")) {
            let list = ObjectRebuilder.classList();

            // Instantiate object of the correct class
            newObj = new (list[obj["__className"]]);

            // Copy all of current object's properties
            // to the newly instantiated object
            newObj = Object.assign(newObj, obj);

            // Run the makeshift constructor, if the
            // new object has one
            if (newObj.__initialize === 'function') {
                newObj.__initialize();
            }
        }

        // Iterate over all of the properties of the new
        // object, and if some of them are objects (or arrays!) 
        // constructed by JSON.parse, run them through ObjectRebuilder
        for (let prop of Object.keys(newObj)) {
            if (ObjectRebuilder.isObject(newObj[prop])) {
                newObj[prop] = ObjectRebuilder.restoreObject(newObj[prop]);
            }
        }

        return newObj;
    }
}

let page = new Page();
let section1 = new PageSection();
let section2 = new PageSection();

page.pageSections = [section1, section2];

let jsonString = JSON.stringify(page);
let restoredPageWithPageSections = ObjectRebuilder.restoreObject(JSON.parse(jsonString));

console.log(restoredPageWithPageSections);

您的页面应该作为类Page的对象进行恢复,其中包含2个类PageSection的对象。无论深度如何,递归都一直工作到最后一个对象。

@Sean Kinsey的回答帮助我找到了解决方案。

答案 3 :(得分:2)

最简单的方法是使用JSON.parse来解析字符串,然后将对象传递给函数。 JSON.parse是在线json2库的一部分。

答案 4 :(得分:1)

万一有人需要它,这里是一个纯粹的JavaScript扩展函数 (这显然属于对象定义)。

  this.extend = function (jsonString){
    var obj = JSON.parse(jsonString)
    for (var key in obj) {
        this[key] = obj[key]
        console.log("Set ", key ," to ", obj[key])
        }   
    } 

请不要忘记删除console.log:P

答案 5 :(得分:0)

我;对此并不多,但aPerson.addAddress不应该工作, 为什么不直接分配到对象?

aPerson.address.push(someAddress);
alert(aPerson.address); // alert [object object]