Question Analyse de la chaîne JSON dans un prototype d'objet particulier en JavaScript


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

C'est génial, mais comment puis-je prendre cet objet JavaScript et le transformer en particulier 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 un objet "Foo". C'est-à-dire que mon objet devrait maintenant avoir une fonction "test" et les propriétés "a" et "b".

METTRE À JOUR Après quelques recherches, j'ai pensé à ça ...

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?

MISE À JOUR Mai 2017: La manière "moderne" de le faire est via Object.assign, mais cette fonction n'est pas disponible dans les navigateurs Android IE 11 ou plus anciens.


114
2018-05-03 17:56


origine


Réponses:


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

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

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

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

41
2018-05-15 10:44



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

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

64
2018-05-03 18:17



Voulez-vous ajouter une fonctionnalité de sérialisation / désérialisation JSON, non? Alors regardez ceci:

Vous voulez y parvenir:

UML

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

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

Usage:

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

Note: Si vous voulez, vous pouvez changer toutes les définitions de propriétés comme this.title, this.author, etc par var title, var author, etc. et leur ajouter des astuces pour accomplir la définition UML.


30
2018-05-03 18:49



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

Vous pouvez utiliser 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 de ce que j'ai lu.


17
2018-05-03 18:24



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

Voici un code simpliste pour la 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 bien, mais comment puis-je prendre cet objet JavaScript et le transformer en un objet JavaScript particulier (c'est-à-dire avec un prototype donné)? contredit le titre, où vous posez des questions sur l'analyse JSON, mais le paragraphe cité concerne le remplacement du prototype d'objet d'exécution JS.


4
2017-07-22 21:53



Par souci d'exhaustivité, voici un simple traceur avec lequel je me suis retrouvé (je n'avais pas besoin de vérifier les propriétés non-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
2018-04-11 16:02



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

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


2
2018-01-16 12:11



Une autre approche pourrait être d'utiliser Object.create. Comme premier argument, vous passez le prototype et, pour le second, vous transmettez une carte des noms de propriété aux 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);


1
2017-08-20 13:00



Bien que ce ne soit pas techniquement ce que vous voulez, si vous connaissez auparavant le type d'objet que vous voulez gérer, vous pouvez utiliser les méthodes call / apply du prototype de votre objet connu.

vous pouvez changer cela

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

pour ça

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

0
2017-12-03 15:57



J'ai combiné les solutions que j'ai pu trouver et compiler en une solution générique capable d'analyser automatiquement un objet personnalisé et tous ses champs de manière récursive afin de pouvoir 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 qu'il saisit chaque objet que vous souhaitez appliquer à son type (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’attribution de type (elle permet d’assigner des types à tous les objets imbriqués de façon récursive; elle parcourt également les tableaux pour trouver 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
2017-12-02 19:11



J'aime ajouter un argument optionnel au constructeur et appeler Object.assign(this, obj), puis gérer 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));
        }
    }
}

0
2018-04-30 00:04