我知道如何解析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浏览器中不可用。
我已经结合了我能够找到的解决方案,并将其编译成一个通用的解决方案,可以自动递归地解析自定义对象及其所有字段,以便在反序列化后使用原型方法。
一个假设是,您定义了一个特殊的字段,该字段在您想要自动应用它的类型的每个对象中指示它的类型。__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);
}
下面是一个使用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应用程序,所以谁在乎呢;-)也许人们至少应该摆脱递归。
几乎没有错误处理:那是另一天的任务了
您想添加JSON序列化/反序列化功能吗?然后看看这个:
你想要达到这个目标:
toJson()是一个普通的方法。
fromJson()是一个静态方法。
实现:
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);
};
用法:
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
注意:如果您愿意,您可以像这样更改所有属性定义。标题,这。通过var title, var author等,并向它们添加getter来完成UML定义。