webentwicklung-frage-antwort-db.com.de

Bester Weg, Objekte in JavaScript zu serialisieren/zu serialisieren?

Ich habe viele JavaScript-Objekte in meiner Anwendung, etwa:

function Person(age) {
    this.age = age;
    this.isOld = function (){
        return this.age > 60;
    }
}
// before serialize, ok
var p1 = new Person(77);
alert("Is old: " + p1.isOld());

// after, got error Object #<Object> has no method 'isOld'
var serialize = JSON.stringify(p1);
var _p1 = JSON.parse(serialize);
alert("Is old: " + _p1.isOld());

Siehe in JS Fiddle .

Meine Frage ist: Gibt es ein bewährtes Verfahren/Muster/Tipp, um mein Objekt in demselben Typ wie vor der Serialisierung (Instanzen der Klasse Person in diesem Fall) wiederherzustellen?

Voraussetzungen, die ich habe:

  • Festplattennutzung optimieren: Ich habe einen großen Baum von Objekten im Speicher. Ich möchte also keine Funktionen speichern.
  • Die Lösung kann jQuery und eine andere Bibliothek zum Serialisieren/Unserialisieren verwenden.
52
Topera

JSON hat keine Funktionen als Datentypen. Sie können nur Strings, Zahlen, Objekte, Arrays und Booleans serialisieren (und null).

Sie können Ihre eigene toJson-Methode erstellen und nur die Daten übergeben, die wirklich serialisiert werden müssen:

Person.prototype.toJson = function() {
    return JSON.stringify({age: this.age});
};

Ähnlich zur Deserialisierung: 

Person.fromJson = function(json) {
    var data = JSON.parse(json); // Parsing the json string.
    return new Person(data.age);
};

Die Verwendung wäre:

var serialize = p1.toJson();
var _p1 = Person.fromJson(serialize);
alert("Is old: " + _p1.isOld());

Um den Arbeitsaufwand zu reduzieren, können Sie in Betracht ziehen, alle Daten, die serialisiert werden müssen, in einer speziellen "data" -Eigenschaft für jede Person-Instanz zu speichern. Zum Beispiel:

function Person(age) {
    this.data = {
        age: age
    };
    this.isOld = function (){
        return this.data.age > 60 ? true : false;
    }
}

beim Serialisieren und Deserialisieren wird dann lediglich JSON.stringify(this.data) aufgerufen, und das Einstellen der Daten einer Instanz wäre instance.data = JSON.parse(json).

Dies würde die toJson- und fromJson-Methoden einfach halten, aber Sie müssten Ihre anderen Funktionen anpassen.


Randnotiz:

Sie sollten die isOld-Methode zum Prototyp der Funktion hinzufügen:

Person.prototype.isOld = function() {}

Ansonsten hat jede Instanz eine eigene Instanz dieser Funktion, wodurch auch der Speicher erhöht wird.

54
Felix Kling

Ich habe serialijse geschrieben, weil ich das gleiche Problem wie Sie hatte.

sie finden es unter https://github.com/erossignon/serialijse

Sie kann in nodejs oder in einem Browser verwendet werden und dient zum Serialisieren und Deserialisieren einer komplexen Menge von Objekten von einem Kontext (nodejs) zum anderen (Browser) oder umgekehrt.

var s = require("serialijse");


var assert = require("assert");


// testing serialization of a simple javascript object with date
function testing_javascript_serialization_object_with_date() {

    var o = {
        date: new Date(),
        name: "foo"
    };
    console.log(o.name, o.date.toISOString());

    // JSON will fail as JSON doesn't preserve dates
    try {
        var jstr = JSON.stringify(o);
        var jo = JSON.parse(jstr);
        console.log(jo.name, jo.date.toISOString());
    } catch (err) {
        console.log(" JSON has failed to preserve Date during stringify/parse ");
        console.log("  and has generated the following error message", err.message);
    }
    console.log("");



    var str = s.serialize(o);
    var so = s.deserialize(str);
    console.log(" However Serialijse knows how to preserve date during serialization/deserialization :");
    console.log(so.name, so.date.toISOString());
    console.log("");
}
testing_javascript_serialization_object_with_date();


// serializing a instance of a class
function testing_javascript_serialization_instance_of_a_class() {

    function Person() {
        this.firstName = "Joe";
        this.lastName = "Doe";
        this.age = 42;
    }

    Person.prototype.fullName = function () {
        return this.firstName + " " + this.lastName;
    };


    // testing serialization using  JSON.stringify/JSON.parse
    var o = new Person();
    console.log(o.fullName(), " age=", o.age);

    try {
        var jstr = JSON.stringify(o);
        var jo = JSON.parse(jstr);
        console.log(jo.fullName(), " age=", jo.age);

    } catch (err) {
        console.log(" JSON has failed to preserve the object class ");
        console.log("  and has generated the following error message", err.message);
    }
    console.log("");

    // now testing serialization using serialijse  serialize/deserialize
    s.declarePersistable(Person);
    var str = s.serialize(o);
    var so = s.deserialize(str);

    console.log(" However Serialijse knows how to preserve object classes serialization/deserialization :");
    console.log(so.fullName(), " age=", so.age);
}
testing_javascript_serialization_instance_of_a_class();


// serializing an object with cyclic dependencies
function testing_javascript_serialization_objects_with_cyclic_dependencies() {

    var Mary = { name: "Mary", friends: [] };
    var Bob = { name: "Bob", friends: [] };

    Mary.friends.Push(Bob);
    Bob.friends.Push(Mary);

    var group = [ Mary, Bob];
    console.log(group);

    // testing serialization using  JSON.stringify/JSON.parse
    try {
        var jstr = JSON.stringify(group);
        var jo = JSON.parse(jstr);
        console.log(jo);

    } catch (err) {
        console.log(" JSON has failed to manage object with cyclic deps");
        console.log("  and has generated the following error message", err.message);
    }

    // now testing serialization using serialijse  serialize/deserialize
    var str = s.serialize(group);
    var so = s.deserialize(str);
    console.log(" However Serialijse knows to manage object with cyclic deps !");
    console.log(so);
    assert(so[0].friends[0] == so[1]); // Mary's friend is Bob
}
testing_javascript_serialization_objects_with_cyclic_dependencies();
6
Etienne

Die native JSON-API des Browsers gibt Ihnen möglicherweise nicht Ihre idOld-Funktion zurück, nachdem Sie JSON.stringify aufgerufen haben, jedoch, wenn Sie Ihre JSON selbst stringifizieren können (verwenden Sie Crockfords json2.js anstelle der Browser-API. Wenn Sie eine Zeichenfolge von JSON haben, z.

var person_json = "{ \"age:\" : 20, \"isOld:\": false, isOld: function() { return this.age > 60; } }";

dann kannst du anrufen 

eval("(" + person + ")") 

und Sie erhalten Ihre Funktion im Json-Objekt zurück.

4
T. Webster

Ich bin der Autor von https://github.com/joonhocho/seri .

Seri ist eine Unterstützung für die benutzerdefinierte (verschachtelte) Klasse JSON +.

Sie müssen lediglich toJSON und fromJSON angeben, um Klasseninstanzen zu serialisieren und deserialisieren.

Hier ein Beispiel mit verschachtelten Klassenobjekten:

import seri from 'seri';

class Item {
  static fromJSON = (name) => new Item(name)

  constructor(name) {
    this.name = name;
  }

  toJSON() {
    return this.name;
  }
}

class Bag {
  static fromJSON = (itemsJson) => new Bag(seri.parse(itemsJson))

  constructor(items) {
    this.items = items;
  }

  toJSON() {
    return seri.stringify(this.items);
  }
}

// register classes
seri.addClass(Item);
seri.addClass(Bag);


const bag = new Bag([
  new Item('Apple'),
  new Item('orange'),
]);


const bagClone = seri.parse(seri.stringify(bag));


// validate
bagClone instanceof Bag;

bagClone.items[0] instanceof Item;
bagClone.items[0].name === 'Apple';

bagClone.items[1] instanceof Item;
bagClone.items[1].name === 'orange';

Ich hoffe, es hilft bei der Lösung Ihres Problems.

2
Joon

Ich hatte ein ähnliches Problem und da ich keine ausreichende Lösung finden konnte, habe ich auch eine Serialisierungsbibliothek für Javascript erstellt: https://github.com/wavesoft/jbb (in der Tat ist es etwas mehr , da es hauptsächlich für die Bündelung von Ressourcen gedacht ist)

Es steht in der Nähe von Binary-JSON, fügt jedoch einige zusätzliche Funktionen hinzu, z. B. Metadaten für die zu kodierenden Objekte und einige zusätzliche Optimierungen wie Datendeduplizierung, Querverweise auf andere Bundles und Kompression auf Strukturebene.

Es gibt jedoch einen Haken: Um die Bündelgröße klein zu halten, enthält das Bündel keine Typinformationen. Diese Informationen werden in einem separaten "Profil" bereitgestellt, das Ihre Objekte zum Kodieren und Dekodieren beschreibt. Aus Optimierungsgründen werden diese Informationen in Form eines Skripts angegeben.

Sie können Ihr Leben jedoch einfacher machen, indem Sie das Dienstprogramm gulp-jbb-profile ( https://github.com/wavesoft/gulp-jbb-profile ) zum Generieren der Codierungs-/Dekodierungsskripten aus einfachen YAML-Objektspezifikationen verwenden, z.

# The 'Person' object has the 'age' and 'isOld'
# properties
Person:
  properties:
    - age
    - isOld

Zum Beispiel können Sie sich das jbb-profile-three-Profil anschauen. Wenn Sie Ihr Profil fertig haben, können Sie JBB folgendermaßen verwenden:

var JBBEncoder = require('jbb/encode');
var MyEncodeProfile = require('profile/profile-encode');

// Create a new bundle
var bundle = new JBBEncoder( 'path/to/bundle.jbb' );

// Add one or more profile(s) in order for JBB
// to understand your custom objects
bundle.addProfile(MyEncodeProfile);

// Encode your object(s) - They can be any valid
// javascript object, or objects described in
// the profiles you added previously.

var p1 = new Person(77);
bundle.encode( p1, 'person' );

var people = [
        new Person(45),
        new Person(77),
        ...
    ];
bundle.encode( people, 'people' );

// Close the bundle when you are done
bundle.close();

Und du kannst es so lesen:

var JBBDecoder = require('jbb/decode');
var MyDecodeProfile = require('profile/profile-decode');

// Instantiate a new binary decoder
var binaryLoader = new JBBDecoder( 'path/to/bundle' );

// Add your decoding profile
binaryLoader.addProfile( MyDecodeProfile );

// Add one or more bundles to load
binaryLoader.add( 'bundle.jbb' );

// Load and callback when ready
binaryLoader.load(function( error, database ) {

    // Your objects are in the database
    // and ready to use!
    var people = database['people'];

});
1
Wavey

Ich hatte genau das gleiche Problem und schrieb ein kleines Werkzeug zum Mischen von Daten und Modell. Siehe https://github.com/khayll/jsmix

So würden Sie es machen:

//model object (or whatever you'd like the implementation to be)
var Person = function() {}
Person.prototype.isOld = function() {
    return this.age > RETIREMENT_AGE;
}

//then you could say:
var result = JSMix(jsonData).withObject(Person.prototype, "persons").build();

//and use
console.log(result.persons[3].isOld());

Es kann auch komplexe Objekte wie verschachtelte Sammlungen rekursiv behandeln.

Bei der Serialisierung von JS-Funktionen würde ich aus Sicherheitsgründen dies nicht tun.

1
fishgen

Ich habe noch ein weiteres JavaScript-Serializer-Repo zu GitHub hinzugefügt.

Anstatt den Ansatz der JavaScript-Objekte in ein internes Format zu serialisieren und zu deserialisieren, besteht der Ansatz hier darin, JavaScript-Objekte in natives JavaScript zu serialisieren. Dies hat den Vorteil, dass das Format vom Serialisierer völlig unabhängig ist und das Objekt einfach durch Aufruf von eval () neu erstellt werden kann.

https://github.com/iconico/JavaScript-Serializer

0
Nico Westerdale