如何使用 JSON 对象初始化 TypeScript 对象? [英] How do I initialize a TypeScript Object with a JSON-Object?

查看:29
本文介绍了如何使用 JSON 对象初始化 TypeScript 对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我从对 REST 服务器的 AJAX 调用收到一个 JSON 对象.此对象具有与我的 TypeScript 类匹配的属性名称(这是 这个问题).

I receive a JSON object from an AJAX call to a REST server. This object has property names that match my TypeScript class (this is a follow-on to this question).

初始化它的最佳方法是什么?我不认为 this 会起作用,因为类 (&JSON 对象)具有作为对象列表的成员和作为类的成员,而这些类具有作为列表和/或类的成员.

What is the best way to initialize it? I don't think this will work because the class (& JSON object) have members that are lists of objects and members that are classes, and those classes have members that are lists and/or classes.

但我更喜欢查找成员名称并分配它们、根据需要创建列表和实例化类的方法,因此我不必为每个类中的每个成员编写显式代码(有很多!)

But I'd prefer an approach that looks up the member names and assigns them across, creating lists and instantiating classes as needed, so I don't have to write explicit code for every member in every class (there's a LOT!)

推荐答案

这些是一些快速截图,以展示几种不同的方法.它们绝不是完整的",作为免责声明,我认为这样做不是一个好主意.代码也不是很干净,因为我只是很快地把它输入在一起.

These are some quick shots at this to show a few different ways. They are by no means "complete" and as a disclaimer, I don't think it's a good idea to do it like this. Also the code isn't too clean since I just typed it together rather quickly.

另请注意:当然,可反序列化的类需要具有默认构造函数,就像我知道任何类型的反序列化的所有其他语言一样.当然,如果你调用一个没有参数的非默认构造函数,Javascript 不会抱怨,但类最好为此做好准备(另外,它不会真的是打字稿方式").

Also as a note: Of course deserializable classes need to have default constructors as is the case in all other languages where I'm aware of deserialization of any kind. Of course, Javascript won't complain if you call a non-default constructor with no arguments, but the class better be prepared for it then (plus, it wouldn't really be the "typescripty way").

这种方法的问题主要是任何成员的名称都必须与其类匹配.这会自动将您限制为每个班级的一个相同类型的成员,并违反了一些良好实践的规则.我强烈建议不要这样做,但只是在这里列出它,因为这是我写这个答案时的第一个草稿"(这也是名称为Foo"等的原因).

The problem with this approach is mostly that the name of any member must match its class. Which automatically limits you to one member of same type per class and breaks several rules of good practice. I strongly advise against this, but just list it here because it was the first "draft" when I wrote this answer (which is also why the names are "Foo" etc.).

module Environment {
    export class Sub {
        id: number;
    }

    export class Foo {
        baz: number;
        Sub: Sub;
    }
}

function deserialize(json, environment, clazz) {
    var instance = new clazz();
    for(var prop in json) {
        if(!json.hasOwnProperty(prop)) {
            continue;
        }

        if(typeof json[prop] === 'object') {
            instance[prop] = deserialize(json[prop], environment, environment[prop]);
        } else {
            instance[prop] = json[prop];
        }
    }

    return instance;
}

var json = {
    baz: 42,
    Sub: {
        id: 1337
    }
};

var instance = deserialize(json, Environment, Environment.Foo);
console.log(instance);

选项 #2:名称 属性

为了摆脱选项 #1 中的问题,我们需要了解 JSON 对象中的节点是什么类型的某种信息.问题在于,在 Typescript 中,这些东西是编译时构造,我们在运行时需要它们——但运行时对象在设置之前根本不知道它们的属性.

Option #2: The name property

To get rid of the problem in option #1, we need to have some kind of information of what type a node in the JSON object is. The problem is that in Typescript, these things are compile-time constructs and we need them at runtime – but runtime objects simply have no awareness of their properties until they are set.

一种方法是让类知道它们的名字.不过,您在 JSON 中也需要此属性.实际上,您在 json 中需要它:

One way to do it is by making classes aware of their names. You need this property in the JSON as well, though. Actually, you only need it in the json:

module Environment {
    export class Member {
        private __name__ = "Member";
        id: number;
    }

    export class ExampleClass {
        private __name__ = "ExampleClass";

        mainId: number;
        firstMember: Member;
        secondMember: Member;
    }
}

function deserialize(json, environment) {
    var instance = new environment[json.__name__]();
    for(var prop in json) {
        if(!json.hasOwnProperty(prop)) {
            continue;
        }

        if(typeof json[prop] === 'object') {
            instance[prop] = deserialize(json[prop], environment);
        } else {
            instance[prop] = json[prop];
        }
    }

    return instance;
}

var json = {
    __name__: "ExampleClass",
    mainId: 42,
    firstMember: {
        __name__: "Member",
        id: 1337
    },
    secondMember: {
        __name__: "Member",
        id: -1
    }
};

var instance = deserialize(json, Environment);
console.log(instance);

选项 #3:明确说明成员类型

如上所述,类成员的类型信息在运行时是不可用的——除非我们使它可用.我们只需要为非原始成员执行此操作,就可以了:

Option #3: Explicitly stating member types

As stated above, the type information of class members is not available at runtime – that is unless we make it available. We only need to do this for non-primitive members and we are good to go:

interface Deserializable {
    getTypes(): Object;
}

class Member implements Deserializable {
    id: number;

    getTypes() {
        // since the only member, id, is primitive, we don't need to
        // return anything here
        return {};
    }
}

class ExampleClass implements Deserializable {
    mainId: number;
    firstMember: Member;
    secondMember: Member;

    getTypes() {
        return {
            // this is the duplication so that we have
            // run-time type information :/
            firstMember: Member,
            secondMember: Member
        };
    }
}

function deserialize(json, clazz) {
    var instance = new clazz(),
        types = instance.getTypes();

    for(var prop in json) {
        if(!json.hasOwnProperty(prop)) {
            continue;
        }

        if(typeof json[prop] === 'object') {
            instance[prop] = deserialize(json[prop], types[prop]);
        } else {
            instance[prop] = json[prop];
        }
    }

    return instance;
}

var json = {
    mainId: 42,
    firstMember: {
        id: 1337
    },
    secondMember: {
        id: -1
    }
};

var instance = deserialize(json, ExampleClass);
console.log(instance);

选项 #4:冗长但简洁的方式

更新 01/03/2016: 正如@GameAlchemist 在评论中指出的那样(想法实现),从 Typescript 1.7 开始,下面描述的解决方案可以使用类/属性装饰器以更好的方式编写.

Option #4: The verbose, but neat way

Update 01/03/2016: As @GameAlchemist pointed out in the comments (idea, implementation), as of Typescript 1.7, the solution described below can be written in a better way using class/property decorators.

序列化总是一个问题,在我看来,最好的方法不是最短的方法.在所有选项中,这是我更喜欢的,因为该类的作者可以完全控制反序列化对象的状态.如果我不得不猜测,我会说所有其他选项迟早会给您带来麻烦(除非 Javascript 提出了一种本机方法来处理这个问题).

Serialization is always a problem and in my opinion, the best way is a way that just isn't the shortest. Out of all the options, this is what I'd prefer because the author of the class has full control over the state of deserialized objects. If I had to guess, I'd say that all other options, sooner or later, will get you in trouble (unless Javascript comes up with a native way for dealing with this).

真的,下面的例子并没有做到灵活性正义.它确实只是复制了类的结构.不过,您在这里必须记住的不同之处在于,该类可以完全控制使用任何类型的 JSON 来控制整个类的状态(您可以计算事物等).

Really, the following example doesn't do the flexibility justice. It really does just copy the class's structure. The difference you have to keep in mind here, though, is that the class has full control to use any kind of JSON it wants to control the state of the entire class (you could calculate things etc.).

interface Serializable<T> {
    deserialize(input: Object): T;
}

class Member implements Serializable<Member> {
    id: number;

    deserialize(input) {
        this.id = input.id;
        return this;
    }
}

class ExampleClass implements Serializable<ExampleClass> {
    mainId: number;
    firstMember: Member;
    secondMember: Member;

    deserialize(input) {
        this.mainId = input.mainId;

        this.firstMember = new Member().deserialize(input.firstMember);
        this.secondMember = new Member().deserialize(input.secondMember);

        return this;
    }
}

var json = {
    mainId: 42,
    firstMember: {
        id: 1337
    },
    secondMember: {
        id: -1
    }
};

var instance = new ExampleClass().deserialize(json);
console.log(instance);

这篇关于如何使用 JSON 对象初始化 TypeScript 对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆