2012-12-25 69 views
9

它很容易使用eval或JSON.parse將JSON加載到JavaScript中的對象中。如何從JSON恢復原始對象/類型?

但是,如果你有一個合適的「類」功能,你如何獲得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創建適當人選的情況下,但我能得到的是一個愚蠢的對象。我想知道它是否可以用原型做些事情?

我不想要解析JSON的每一行,並將每個變量分配給相應的函數屬性,這太難了。實際的JSON和函數比上面的例子複雜得多。

我假設可以JSON化函數方法到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 

任何想法?

+2

請使用['JSON.parse'](http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/)而不是'eval'來解析JSON。如果目標瀏覽器不支持'JSON.parse'(即IE7或更舊版本),則可以通過Crockford的'json.js'(它在內部使用'eval',但也提供了完整性驗證)或'json2'。 js'是隨時可用的。 – 2012-12-25 02:19:47

+0

嗨,問題不是eval或解析,這兩個產生相同的無類型對象,我試圖找到一種方法來獲取JSON到人功能。對此有何想法? –

+0

@JohnLittle永遠不會使用eval!,它是許多安全問題的源頭(代碼注入等)[閱讀全文](http://stackoverflow.com/questions/197769/when-is-javascripts-eval-not-vil ) – thepoosh

回答

2

最簡單的方法是使用JSON.parse解析你的字符串,然後將該對象傳遞給函數。 JSON.parse是在線json2庫的一部分。

+1

JSON方法在現代瀏覽器中可用...使用庫作爲舊版瀏覽器的回退 – charlietfl

+0

JSON.stringify()函數對於*解析*而言不是**;它用於將對象*轉換爲JSON字符串。 – Pointy

+0

嗨,這是關鍵,如何將產生的複雜對象轉變爲功能?你不能傳遞它 - 除非我寫了一些非常複雜的東西來遍歷對象,把每個子對象和數據項都複製到Person對象中,我不想這麼做,尤其是當它經常變化時。 –

8

許多框架提供了一個「擴展」功能,可以將字段從一個對象複製到另一個對象。你可以把它和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() 
+0

冒着引起混亂的風險,Coffeescript +1! –

0

我,M不是太多到這一點,但aPerson.addAddress不應該工作, 爲什麼不直接分配到對象?

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

您需要使用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

是否有一個公約來調用這個函數'reviver'? – Bergi

+1

在JSON對象的規範中,方法(和形式參數)被稱爲reviver。 –

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

1

有點遲到了,但是這可能幫助別人。 這是我是如何解決它,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類的一個對象,包含PageSection類的2個對象數組。無論深度如何,遞歸都可以一直工作到最後一個對象。

@Sean Kinsey的回答幫助我找到了解決方案。