如何使用 JSON 对象初始化 TypeScript 对象?

新手上路,请多包涵

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

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

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

原文由 David Thielen 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.3k
2 个回答

这些是一些快速的镜头,以展示几种不同的方式。它们绝不是“完整的”,作为免责声明,我认为这样做不是一个好主意。而且代码也不是很干净,因为我只是很快地把它一起输入了。

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

选项 #1:根本没有运行时信息

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

 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 中,这些东西是编译时构造,我们在运行时需要它们——但运行时对象在设置它们之前根本不知道它们的属性。

一种方法是让类知道他们的名字。不过,您在 JSON 中也需要此属性。实际上,您 需要在 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:明确说明成员类型

如上所述,类成员的类型信息在运行时不可用——除非我们使其可用。我们只需要对非原始成员执行此操作即可:

 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:冗长但简洁的方式

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

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

真的,下面的例子并没有做到灵活性。它确实只是复制了类的结构。但是,您必须记住的区别在于,该类可以完全控制使用它想要控制整个类的状态的任何类型的 JSON(您可以计算事物等)。

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

原文由 Ingo Bürk 发布,翻译遵循 CC BY-SA 4.0 许可协议

如果你想要类型安全并且不喜欢装饰器

abstract class IPerson{
  name?: string;
  age?: number;
}
class Person extends IPerson{
  constructor({name, age}: IPerson){
    super();
    this.name = name;
    this.age = age;
  }
}

const json = {name: "ali", age: 80};
const person = new Person(json);

或者这个我更喜欢

class Person {
  constructor(init?: Partial<Person>){
      Object.assign(this, init);
  }
  name?: string;
  age?: number;
}

const json = {name: "ali", age: 80};
const person = new Person(json);

原文由 Ali80 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进