将 JSON 字符串解析为 JavaScript 中的特定对象原型

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5873624/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 19:18:26  来源:igfitidea点击:

Parse JSON String into a Particular Object Prototype in JavaScript

javascriptjsonparsingobjectprototype

提问by BMiner

I know how to parse a JSON String and turn it into a JavaScript Object. You can use JSON.parse()in modern browsers (and IE9+).

我知道如何解析 JSON 字符串并将其转换为 JavaScript 对象。您可以JSON.parse()在现代浏览器(和 IE9+)中使用。

That's great, but how can I take that JavaScript Object and turn it into a particularJavaScript Object (i.e. with a certain prototype)?

太好了,但是我怎样才能将该 JavaScript 对象转换为特定的JavaScript 对象(即具有特定原型)?

For example, suppose you have:

例如,假设您有:

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

Again, I am not wondering how to convert a JSON string into a generic JavaScript Object. I want to know how to convert a JSON string into a "Foo" Object. That is, my Object should now have a function 'test' and properties 'a' and 'b'.

同样,我并不想知道如何将 JSON 字符串转换为通用 JavaScript 对象。我想知道如何将 JSON 字符串转换为“Foo”对象。也就是说,我的对象现在应该有一个函数“test”和属性“a”和“b”。

UPDATEAfter doing some research, I thought of this...

更新在做了一些研究之后,我想到了这个......

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

Will that work?

那行得通吗?

UPDATE May, 2017: The "modern" way of doing this, is via Object.assign, but this function is not available in IE 11 or older Android browsers.

2017 年 5 月更新:执行此操作的“现代”方式是通过Object.assign,但此功能在 IE 11 或更旧的 Android 浏览器中不可用。

采纳答案by Erik van Velzen

The current answers contain a lot of hand-rolled or library code. This is not necessary.

当前的答案包含大量手卷或库代码。这是没有必要的。

  1. Use JSON.parse('{"a":1}')to create a plain object.

  2. Use one of the standardized functions to set the prototype:

    • Object.assign(new Foo, { a: 1 })
    • Object.setPrototypeOf({ a: 1 }, Foo.prototype)
  1. 使用JSON.parse('{"a":1}')创建一个普通的对象。

  2. 使用标准化函数之一来设置原型:

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

回答by Oliver Moran

See an example below (this example uses the native JSON object). My changes are commented in CAPITALS:

请参阅下面的示例(此示例使用本机 JSON 对象)。我的更改在大写中注释:

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

回答by Gabriel Llamas

Do you want to add JSON serialization/deserialization functionality, right? Then look at this:

You want to achieve this:

您要添加 JSON 序列化/反序列化功能,对吗?然后看看这个:

你想实现这个:

UML

UML

toJson() is a normal method.
fromJson() is a static method.

toJson() 是一个普通的方法。
fromJson() 是一个静态方法。

Implementation:

实施

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: If you want you can change all property definitions like this.title, this.author, etc by var title, var author, etc. and add getters to them to accomplish the UML definition.

注意:如果您愿意,您可以通过、 等更改所有属性定义,例如this.titlethis.authorvar titlevar author并向它们添加 getter 以完成 UML 定义。

回答by BMiner

A blog post that I found useful: Understanding JavaScript Prototypes

我发现有用的一篇博文: Understanding JavaScript Prototypes

You can mess with the __proto__ property of the Object.

你可以弄乱对象的 __proto__ 属性。

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

This allows fooJSON to inherit the Foo prototype.

这允许 fooJSON 继承 Foo 原型。

I don't think this works in IE, though... at least from what I've read.

我不认为这在 IE 中有效,但是......至少从我读过的内容来看。

回答by Philipp Munin

Am I missing something in the question or why else nobody mentioned reviverparameter of JSON.parsesince 2011?

我是否遗漏了问题中的某些内容,或者为什么没有人提到自 2011 年以来的reviver参数JSON.parse

Here is simplistic code for solution that works: https://jsfiddle.net/Ldr2utrr/

这是有效的解决方案的简单代码: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: Your question is confusing: >>That's great, but how can I take that JavaScript Object and turn it into a particular JavaScript Object (i.e. with a certain prototype)?contradicts to the title, where you ask about JSON parsing, but the quoted paragraph asks about JS runtime object prototype replacement.

PS:您的问题令人困惑:>>太好了,但是我如何才能将该 JavaScript 对象转换为特定的 JavaScript 对象(即具有特定原型)?与标题相矛盾,在那里您询问 JSON 解析,但引用的段落询问 JS 运行时对象原型替换。

回答by Matías Fidemraizer

An alternate approach could be using Object.create. As first argument, you pass the prototype, and for the second one you pass a map of property names to descriptors:

另一种方法是使用Object.create. 作为第一个参数,您传递原型,对于第二个参数,您将属性名称的映射传递给描述符:

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

回答by Jason Goemaat

I like adding an optional argument to the constructor and calling Object.assign(this, obj), then handling any properties that are objects or arrays of objects themselves:

我喜欢向构造函数添加一个可选参数并调用Object.assign(this, obj),然后处理作为对象或对象数组本身的任何属性:

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

回答by skerit

I created a package called json-dry. It supports (circular) references and also class instances.

我创建了一个名为json-dry的包。它支持(循环)引用和类实例。

You have to define 2 new methods in your class (toDryon the prototype and unDryas a static method), register the class (Dry.registerClass), and off you go.

您必须在类中定义 2 个新方法(toDry在原型上和unDry作为静态方法),注册类 ( Dry.registerClass),然后就可以了。

回答by Rob

For the sake of completeness, here's a simple one-liner I ended up with (I had no need checking for non-Foo-properties):

为了完整起见,这是我最终得到的一个简单的单行(我不需要检查非 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 }'));

回答by vir us

I've combined the solutions that I was able to find and compiled it into a generic one that can automatically parse a custom object and all it's fields recursively so you can use prototype methods after deserialization.

我已经组合了我能够找到的解决方案并将其编译成一个通用的解决方案,该解决方案可以自动解析自定义对象及其所有字段,以便您可以在反序列化后使用原型方法。

One assumption is that you defined a special filed that indicates it's type in every object you want to apply it's type automatically (this.__typein the example).

一个假设是您定义了一个特殊字段,该字段指示您要自动应用其类型的每个对象中的类型(this.__type在示例中)。

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
      //...
}

Type assignment function (it work recursively to assign types to any nested objects; it also iterates through arrays to find any suitable objects):

类型赋值函数(它递归地为任何嵌套对象分配类型;它还遍历数组以找到任何合适的对象):

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