web-dev-qa-db-fra.com

Analyser la chaîne JSON dans un prototype d'objet particulier en JavaScript

Je sais comment analyser une chaîne JSON et la transformer en objet JavaScript. Vous pouvez utiliser JSON.parse() dans les navigateurs modernes (et IE9 +).

C'est bien, mais comment puis-je prendre cet objet JavaScript et le transformer en particular == objet JavaScript (c'est-à-dire avec un certain prototype)?

Par exemple, supposons que vous ayez:

_function Foo()
{
   this.a = 3;
   this.b = 2;
   this.test = function() {return this.a*this.b;};
}
var fooObj = new Foo();
alert(fooObj.test() ); //Prints 6
var fooJSON = JSON.parse({"a":4, "b": 3});
//Something to convert fooJSON into a Foo Object
//....... (this is what I am missing)
alert(fooJSON.test() ); //Prints 12
_

Encore une fois, je ne me demande pas comment convertir une chaîne JSON en un objet JavaScript générique. Je veux savoir comment convertir une chaîne JSON en objet "Foo". C'est-à-dire que mon objet devrait maintenant avoir une fonction "test" et des propriétés "a" et "b".

UPDATE Après quelques recherches, j'ai pensé à cela ...

_Object.cast = function cast(rawObj, constructor)
{
    var obj = new constructor();
    for(var i in rawObj)
        obj[i] = rawObj[i];
    return obj;
}
var fooJSON = Object.cast({"a":4, "b": 3}, Foo);
_

Ça marchera?

UPDATE mai 2017 : La manière "moderne" de procéder est via Object.assign , mais cette fonction n'est pas disponible dans les navigateurs IE 11 ou versions antérieures Android.

155
BMiner

Les réponses actuelles contiennent beaucoup de code de bibliothèque ou de roulé à la main. Ce n'est pas nécessaire.

  1. Utilisez JSON.parse('{"a":1}') pour créer un objet simple.

  2. Utilisez l'une des fonctions normalisées pour définir le prototype:

    • Object.assign(new Foo, { a: 1 })
    • Object.setPrototypeOf({ a: 1 }, Foo.prototype)
85
Erik van Velzen

Voir un exemple ci-dessous (cet exemple utilise l'objet JSON natif). Mes modifications sont commentées en majuscules:

function Foo(obj) // CONSTRUCTOR CAN BE OVERLOADED WITH AN OBJECT
{
    this.a = 3;
    this.b = 2;
    this.test = function() {return this.a*this.b;};

    // IF AN OBJECT WAS PASSED THEN INITIALISE PROPERTIES FROM THAT OBJECT
    for (var prop in obj) this[prop] = obj[prop];
}

var fooObj = new Foo();
alert(fooObj.test() ); //Prints 6

// INITIALISE A NEW FOO AND PASS THE PARSED JSON OBJECT TO IT
var fooJSON = new Foo(JSON.parse('{"a":4,"b":3}'));

alert(fooJSON.test() ); //Prints 12
70
Oliver Moran

Voulez-vous ajouter une fonctionnalité de sérialisation/désérialisation JSON, n'est-ce pas? Alors regarde ça:

Vous voulez réaliser ceci:

UML

toJson () est une méthode normale.
fromJson () est une méthode statique.

Mise en oeuvre:

var Book = function (title, author, isbn, price, stock){
    this.title = title;
    this.author = author;
    this.isbn = isbn;
    this.price = price;
    this.stock = stock;

    this.toJson = function (){
        return ("{" +
            "\"title\":\"" + this.title + "\"," +
            "\"author\":\"" + this.author + "\"," +
            "\"isbn\":\"" + this.isbn + "\"," +
            "\"price\":" + this.price + "," +
            "\"stock\":" + this.stock +
        "}");
    };
};

Book.fromJson = function (json){
    var obj = JSON.parse (json);
    return new Book (obj.title, obj.author, obj.isbn, obj.price, obj.stock);
};

sage:

var book = new Book ("t", "a", "i", 10, 10);
var json = book.toJson ();
alert (json); //prints: {"title":"t","author":"a","isbn":"i","price":10,"stock":10}

var book = Book.fromJson (json);
alert (book.title); //prints: t

Remarque: Si vous le souhaitez, vous pouvez modifier toutes les définitions de propriétés telles que this.title, this.author, etc. par var title, var author, etc., et y ajouter des accesseurs pour atteindre la définition UML. .

40
Gabriel Llamas

Un article de blog que j'ai trouvé utile: Comprendre les prototypes JavaScript

Vous pouvez jouer avec la propriété __proto__ de l'objet.

var fooJSON = jQuery.parseJSON({"a":4, "b": 3});
fooJSON.__proto__ = Foo.prototype;

Cela permet à fooJSON d'hériter du prototype Foo.

Je ne pense pas que cela fonctionne dans IE, cependant ... du moins d'après ce que j'ai lu.

18
BMiner

Est-ce qu'il me manque quelque chose dans la question ou pourquoi personne d'autre n'est mentionné reviver paramètre de JSON.parse depuis 2011?

Voici un code simpliste pour une solution qui fonctionne: https://jsfiddle.net/Ldr2utrr/

_function Foo()
{
   this.a = 3;
   this.b = 2;
   this.test = function() {return this.a*this.b;};
}


var fooObj = new Foo();
alert(fooObj.test() ); //Prints 6
var fooJSON = JSON.parse(`{"a":4, "b": 3}`, function(key,value){
if(key!=="") return value; //logic of course should be more complex for handling nested objects etc.
  let res = new Foo();
  res.a = value.a;
  res.b = value.b;
  return res;
});
// Here you already get Foo object back
alert(fooJSON.test() ); //Prints 12
_

PS: Votre question est confuse: >> C'est génial, mais comment puis-je prendre cet objet JavaScript et le transformer en un objet JavaScript particulier (c'est-à-dire avec un certain prototype)? contredit le titre, où vous posez des questions sur l'analyse JSON, mais le paragraphe cité pose des questions sur le remplacement du prototype d'objet d'exécution JS.

7
Philipp Munin

Une autre approche pourrait utiliser Object.create . En premier argument, vous transmettez le prototype et, pour le second, une carte de noms de propriétés à des descripteurs:

_function SomeConstructor() {
  
};

SomeConstructor.prototype = {
  doStuff: function() {
      console.log("Some stuff"); 
  }
};

var jsonText = '{ "text": "hello wrold" }';
var deserialized = JSON.parse(jsonText);

// This will build a property to descriptor map
// required for #2 argument of Object.create
var descriptors = Object.keys(deserialized)
  .reduce(function(result, property) {
    result[property] = Object.getOwnPropertyDescriptor(deserialized, property);
  }, {});

var obj = Object.create(SomeConstructor.prototype, descriptors);_
2
Matías Fidemraizer

J'ai créé un paquet nommé json-dry . Il prend en charge les références (circulaires) et les instances de classe.

Vous devez définir 2 nouvelles méthodes dans votre classe (toDry sur le prototype et unDry en tant que méthode statique), enregistrez la classe (Dry.registerClass), et c'est parti.

2
skerit

Par souci d'exhaustivité, voici une simple ligne à laquelle je me suis retrouvé (je n'avais pas besoin de vérifier les propriétés autres que Foo):

var Foo = function(){ this.bar = 1; };

// angular version
var foo = angular.extend(new Foo(), angular.fromJson('{ "bar" : 2 }'));

// jquery version
var foo = jQuery.extend(new Foo(), jQuery.parseJSON('{ "bar" : 3 }'));
2
Rob

J'aime ajouter un argument optionnel au constructeur et appeler Object.assign(this, obj), puis gérer toutes les propriétés qui sont des objets ou des tableaux d'objets eux-mêmes:

constructor(obj) {
    if (obj != null) {
        Object.assign(this, obj);
        if (this.ingredients != null) {
            this.ingredients = this.ingredients.map(x => new Ingredient(x));
        }
    }
}
1
Jason Goemaat

Bien que, techniquement, ce ne soit pas ce que vous souhaitiez, si vous connaissez bien le type d'objet que vous souhaitez gérer, vous pouvez utiliser les méthodes appel/application du prototype de votre objet connu.

tu peux changer ça

alert(fooJSON.test() ); //Prints 12

pour ça

alert(Foo.prototype.test.call(fooJSON); //Prints 12
0
Remus

J'ai combiné les solutions que j'ai pu trouver et les ai compilées dans une solution générique capable d'analyser automatiquement un objet personnalisé et tous ses champs de manière à ce que vous puissiez utiliser des méthodes prototypes après la désérialisation.

Une hypothèse est que vous avez défini un fichier spécial qui indique son type dans chaque objet que vous souhaitez appliquer, son type automatiquement (this.__type dans l'exemple).

function Msg(data) {
    //... your init code
    this.data = data //can be another object or an array of objects of custom types. 
                     //If those objects defines `this.__type', their types will be assigned automatically as well
    this.__type = "Msg"; // <- store the object's type to assign it automatically
}

Msg.prototype = {
    createErrorMsg: function(errorMsg){
        return new Msg(0, null, errorMsg)
    },
    isSuccess: function(){
        return this.errorMsg == null;
    }
}

usage:

var responseMsg = //json string of Msg object received;
responseMsg = assignType(responseMsg);

if(responseMsg.isSuccess()){ // isSuccess() is now available
      //furhter logic
      //...
}

Fonction d’affectation de types (attribuer de manière récursive des types à des objets imbriqués; elle effectue également une itération dans des tableaux pour rechercher les objets appropriés):

function assignType(object){
    if(object && typeof(object) === 'object' && window[object.__type]) {
        object = assignTypeRecursion(object.__type, object);
    }
    return object;
}

function assignTypeRecursion(type, object){
    for (var key in object) {
        if (object.hasOwnProperty(key)) {
            var obj = object[key];
            if(Array.isArray(obj)){
                 for(var i = 0; i < obj.length; ++i){
                     var arrItem = obj[i];
                     if(arrItem && typeof(arrItem) === 'object' && window[arrItem.__type]) {
                         obj[i] = assignTypeRecursion(arrItem.__type, arrItem);
                     }
                 }
            } else  if(obj && typeof(obj) === 'object' && window[obj.__type]) {
                object[key] = assignTypeRecursion(obj.__type, obj);
            }
        }
    }
    return Object.assign(new window[type](), object);
}
0
vir us