2012-12-25 13 views
9

È facile caricare JSON in un oggetto in javascript utilizzando eval o JSON.parse.Come ripristinare l'oggetto/tipo originale da JSON?

Ma se si dispone di una funzione di tipo "class" appropriata, come vengono inseriti i dati 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! 

Il punto cruciale è che ho bisogno di essere in grado di creare istanze persona adatta da JSON, ma tutto quello che posso ottenere è un oggetto muto. Mi chiedo se è possibile fare qualcosa con i prototipi?

Non voglio analizzare ogni riga del JSON e assegnare ogni variabile agli attributi delle funzioni corrispondenti, il che sarebbe troppo difficile. Il reale JSON e le funzioni che ho sono molto più complicate rispetto all'esempio sopra.

Io parto dal presupposto si potrebbe JSONify metodi funzioni nella stringa JSON, ma come ho bisogno di mantenere i dati risultanti più piccolo possibile questa non è un'opzione - voglio solo per memorizzare e caricare i dati, non il javascript codice per i metodi.

Inoltre, non desidero dovere inserire i dati caricati da JSON come oggetto secondario se posso aiutarlo (ma potrebbe essere l'unico modo), ad es.

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

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

Qualche idea?

+2

Utilizzare ['JSON.parse'] (http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/) e non' eval' per analizzare JSON. Se i browser di destinazione non supportano 'JSON.parse' (cioè IE7 o precedente), uno shim via' json.js' di Crockford (che usa internamente eval', ma fornisce anche validazione di sanità) o 'json2. js' è prontamente disponibile. –

+0

Ciao, Il problema non è eval o parse, che producono entrambi lo stesso oggetto non tipizzato, sto cercando di trovare un modo per far entrare il JSON nella Fuction della Person. Qualche idea su questo? –

+0

@JohnLittle non usa mai eval !, è una fonte di molti problemi di sicurezza (iniezione di codice, ecc.) [Leggi questo] (http://stackoverflow.com/questions/197769/when-is-javascripts-eval-not-evil) – thepoosh

risposta

2

Il modo più semplice è utilizzare JSON.parse per analizzare la stringa e quindi passare l'oggetto alla funzione. JSON.parse fa parte della libreria json2 online.

+1

I metodi JSON sono disponibili nei browser moderni ... usa la libreria come fallback per i browser più vecchi – charlietfl

+0

La funzione 'JSON.stringify()' è ** non ** per * parsing *; serve per trasformare un oggetto * in * una stringa JSON. – Pointy

+0

Ciao, questo è il punto cruciale, come trasformare l'oggetto complesso risultante in intuction? Non puoi semplicemente passarlo - a meno che non abbia scritto qualcosa di molto complesso per attraversare l'oggetto, copiando ogni sotto-oggetto e dato nell'obitorio della Persona che non voglio dover fare, specialmente se cambia frequentemente. –

8

Molti framework forniscono una funzione 'estendi' che copierà i campi da un oggetto all'altro. Puoi combinarlo con JSON.parse per fare ciò che vuoi.

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

Se non si desidera includere qualcosa come il carattere di sottolineatura, è sempre possibile copiare solo la funzione di estensione o scrivere la propria.

CoffeeScript esempio perché mi annoiavo:

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

A rischio di incorrere in ira miscellanea, +1 per Coffeescript! –

0

I; m non troppo in questo, ma aPerson.addAddress non dovrebbe funzionare, perché non assegnare in oggetto direttamente?

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

è necessario utilizzare una funzione 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 

Nessun altro modo davvero ...

+1

Esiste una convenzione per chiamare questa funzione 'reviver'? – Bergi

+1

Nella specifica per l'oggetto JSON, il metodo (e l'argomento formale) viene indicato come reviver. –

1

Solo nel caso qualcuno ne ha bisogno, ecco un javascript pura funzione di estendere (questo ovviamente apparterrebbe ad una definizione di oggetto).

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]) 
     } 
    } 

Si prega di non dimenticare di rimuovere il console.log: Un po 'P

1

in ritardo alla festa, ma questo potrebbe aiutare qualcuno. Ecco come ho risolto, ES6 sintassi:

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); 

La pagina dovrebbe essere ripristinato come un oggetto della classe Page, con array contenente 2 oggetti della classe PageSection. La ricorsione funziona fino all'ultimo oggetto indipendentemente dalla profondità.

@Sean La risposta di Kinsey mi ha aiutato ad arrivare alla mia soluzione.

Problemi correlati