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

这很好,但我怎么能把这个JavaScript对象,并把它变成一个特定的JavaScript对象(即与特定的原型)?

例如,假设你有:

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

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

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

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

这样能行吗?

2017年5月更新:“现代”的方式是通过对象。但该功能在ie11或更老的Android浏览器中不可用。


当前回答

A类{ 构造函数(a) { 这一点。A = A } Method1 () { console.log(“嗨”) } } var b = new A(1) B.method1 () // hi var c = JSON.stringify(b) var d = JSON.parse(c) Console.log (d.a) // 1 尝试{ D.method1() //不是函数 } catch { Console.log('不是函数') } var e =对象。A.prototype setPrototypeOf (d) E.method1 () // hi

其他回答

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

使用JSON.parse('{"a":1}')创建一个普通对象。 使用其中一个标准化函数来设置原型: 对象。assign(new Foo, {a: 1}) 对象。setPrototypeOf({a: 1}, Foo.prototype)

我是否在问题中遗漏了一些东西,或者为什么没有人提到JSON的恢复参数。从2011年开始解析?

下面是简单的解决方案代码: 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:你的问题令人困惑:>>这很好,但我如何将JavaScript对象转化为特定的JavaScript对象(即具有特定的原型)? 与标题相矛盾的是,你问的是JSON解析,但引用的段落问的是JS运行时对象原型替换。

然而,从技术上讲,这并不是你想要的,如果你事先知道你想要处理的对象类型,你可以使用已知对象原型的call/apply方法。

你可以改变这个

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

这个

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

下面是一个使用typescript和decorator的解决方案。

对象在反序列化后保留它们的方法 空对象及其子对象是默认初始化的

如何使用:

@SerializableClass
class SomeClass {
  serializedPrimitive: string;

  @SerializableProp(OtherSerializedClass)
  complexSerialized = new OtherSerializedClass();
}

@SerializableClass
class OtherSerializedClass {
  anotherPrimitive: number;

  someFunction(): void {
  }
}

const obj = new SomeClass();
const json = Serializable.serializeObject(obj);
let deserialized = new SomeClass();
Serializable.deserializeObject(deserialized, JSON.parse(json));
deserialized.complexSerialized.someFunction(); // this works!

它是如何工作的

序列化:

在原型中存储类型名称(__typeName) 使用JSON。stringify使用一个替换方法,将__typeName添加到JSON中。

反序列化:

将所有可序列化类型存储在serializable . __serializableobjects中 在每个对象中存储一个复杂类型属性列表(__serializedProps) 通过类型名和__serializableObjects初始化一个对象theObject。 通过对象。__serializedProps并递归遍历它(从最后一步开始,每一个序列化属性)。将结果分配给according属性。 使用对象。Assign用于分配所有剩余的原语属性。

代码:

// @Class decorator for serializable objects
export function SerializableClass(targetClass): void {
    targetClass.prototype.__typeName = targetClass.name;
    Serializable.__serializableObjects[targetClass.name] = targetClass;
}

// @Property decorator for serializable properties
export function SerializableProp(objectType: any) {
    return (target: {} | any, name?: PropertyKey): any => {
        if (!target.constructor.prototype?.__serializedProps)
            target.constructor.prototype.__serializedProps = {};
        target.constructor.prototype.__serializedProps[name] = objectType.name;
    };
}

export default class Serializable {
    public static __serializableObjects: any = {};

    private constructor() {
        // don't inherit from me!
    }

    static serializeObject(typedObject: object) {
        return JSON.stringify(typedObject, (key, value) => {
                if (value) {
                    const proto = Object.getPrototypeOf(value);
                    if (proto?.__typeName)
                        value.__typeName = proto.__typeName;
                }
                return value;
            }
        );
    }

    static deserializeObject(typedObject: object, jsonObject: object): object {
        const typeName = typedObject.__typeName;
        return Object.assign(typedObject, this.assignTypeRecursion(typeName, jsonObject));
    }

    private static assignTypeRecursion(typeName, object): object {
        const theObject = new Serializable.__serializableObjects[typeName]();
        Object.assign(theObject, object);
        const props = Object.getPrototypeOf(theObject).__serializedProps;
        for (const property in props) {
            const type = props[property];
            try {
                if (type == Array.name) {
                    const obj = object[property];
                    if (Array.isArray(obj)) {
                        for (let i = 0; i < obj.length; ++i) {
                            const arrItem = obj[i];
                            obj[i] = Serializable.assignTypeRecursion(arrItem.__typeName, arrItem);
                        }
                    } else
                        object[property] = [];
                } else
                    object[property] = Serializable.assignTypeRecursion(type, object[property]);
            } catch (e) {
                console.error(`${e.message}: ${type}`);
            }
        }
        return theObject;
    }
}

评论 因为我是一个完全的js/ts新手(< 10天),我很高兴收到任何输入/评论/建议。以下是我目前的一些想法:

它可以更简洁:不幸的是,我没有找到一种方法来消除@SerializableProp的冗余参数。

它可以对内存更友好:在调用serializeObject()之后,每个对象都会存储__typeName,这可能会大量增加内存占用。幸运的是,__serializedProps每个类只存储一次。

它可以对CPU更友好:这是我写过的最低效的代码。但好吧,它只是用于web应用程序,所以谁在乎呢;-)也许人们至少应该摆脱递归。

几乎没有错误处理:那是另一天的任务了

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

一个假设是,您定义了一个特殊的字段,该字段在您想要自动应用它的类型的每个对象中指示它的类型。__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;
    }
}

用法:

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

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

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

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