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

初始化它的最佳方法是什么?我不认为这将工作,因为类(& JSON对象)的成员是对象的列表和成员是类,而这些类的成员是列表和/或类。

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


当前回答

你可以像下面这样做

export interface Instance {
  id?:string;
  name?:string;
  type:string;
}

and

var instance: Instance = <Instance>({
      id: null,
      name: '',
      type: ''
    });

其他回答

也许不现实,但简单的解决方案:

interface Bar{
x:number;
y?:string; 
}

var baz:Bar = JSON.parse(jsonString);
alert(baz.y);

对困难的依赖也要努力!!

JQuery .extend为你做这个:

var mytsobject = new mytsobject();

var newObj = {a:1,b:2};

$.extend(mytsobject, newObj); //mytsobject will now contain a & b

你可以使用Object。我不知道这是什么时候添加的,我目前使用的是Typescript 2.0.2,这似乎是ES6的一个特性。

client.fetch( '' ).then( response => {
        return response.json();
    } ).then( json => {
        let hal : HalJson = Object.assign( new HalJson(), json );
        log.debug( "json", hal );

这是HalJson

export class HalJson {
    _links: HalLinks;
}

export class HalLinks implements Links {
}

export interface Links {
    readonly [text: string]: Link;
}

export interface Link {
    readonly href: URL;
}

这是chrome说的

HalJson {_links: Object}
_links
:
Object
public
:
Object
href
:
"http://localhost:9000/v0/public

你可以看到它没有递归地赋值

我已经创建了一个生成TypeScript接口和运行时“类型映射”的工具,用于对JSON的结果执行运行时类型检查。解析:ts.quicktype.io

例如,给定这个JSON:

{
  "name": "David",
  "pets": [
    {
      "name": "Smoochie",
      "species": "rhino"
    }
  ]
}

quicktype生成以下TypeScript接口和类型映射:

export interface Person {
    name: string;
    pets: Pet[];
}

export interface Pet {
    name:    string;
    species: string;
}

const typeMap: any = {
    Person: {
        name: "string",
        pets: array(object("Pet")),
    },
    Pet: {
        name: "string",
        species: "string",
    },
};

然后检查JSON的结果。根据类型映射解析:

export function fromJson(json: string): Person {
    return cast(JSON.parse(json), object("Person"));
}

我省略了一些代码,但您可以尝试快速输入细节。

TLDR: typejjson(概念的工作证明)


这个问题复杂的根源在于,我们需要在运行时使用只存在于编译时的类型信息反序列化JSON。这要求类型信息在运行时以某种方式可用。

幸运的是,这个问题可以用装饰器和ReflectDecorators以一种非常优雅和健壮的方式解决:

在受序列化影响的属性上使用属性装饰器,以记录元数据信息并将该信息存储在某个地方,例如在类原型上 将此元数据信息提供给递归初始化器(反序列化器)

 

记录类型信息

结合使用reflectdecorator和属性decorator,可以很容易地记录关于属性的类型信息。这种方法的基本实现是:

function JsonMember(target: any, propertyKey: string) {
    var metadataFieldKey = "__propertyTypes__";

    // Get the already recorded type-information from target, or create
    // empty object if this is the first property.
    var propertyTypes = target[metadataFieldKey] || (target[metadataFieldKey] = {});

    // Get the constructor reference of the current property.
    // This is provided by TypeScript, built-in (make sure to enable emit
    // decorator metadata).
    propertyTypes[propertyKey] = Reflect.getMetadata("design:type", target, propertyKey);
}

对于任何给定的属性,上面的代码段将向类原型上隐藏的__propertyTypes__属性添加该属性的构造函数的引用。例如:

class Language {
    @JsonMember // String
    name: string;

    @JsonMember// Number
    level: number;
}

class Person {
    @JsonMember // String
    name: string;

    @JsonMember// Language
    language: Language;
}

就这样,我们在运行时获得了所需的类型信息,现在可以对其进行处理了。

 

处理类型信息

我们首先需要使用JSON获取一个Object实例。解析——之后,我们可以遍历__propertyTypes__(上面收集的)中的整个对象,并相应地实例化所需的属性。必须指定根对象的类型,以便反序列化程序有一个起始点。

同样,这个方法的一个非常简单的实现是:

function deserialize<T>(jsonObject: any, Constructor: { new (): T }): T {
    if (!Constructor || !Constructor.prototype.__propertyTypes__ || !jsonObject || typeof jsonObject !== "object") {
        // No root-type with usable type-information is available.
        return jsonObject;
    }

    // Create an instance of root-type.
    var instance: any = new Constructor();

    // For each property marked with @JsonMember, do...
    Object.keys(Constructor.prototype.__propertyTypes__).forEach(propertyKey => {
        var PropertyType = Constructor.prototype.__propertyTypes__[propertyKey];

        // Deserialize recursively, treat property type as root-type.
        instance[propertyKey] = deserialize(jsonObject[propertyKey], PropertyType);
    });

    return instance;
}
var json = '{ "name": "John Doe", "language": { "name": "en", "level": 5 } }';
var person: Person = deserialize(JSON.parse(json), Person);

上面的想法有一个很大的优点,即按预期的类型(对于复杂/对象值)反序列化,而不是按JSON中呈现的内容反序列化。如果期望Person,则创建的是Person实例。通过对基本类型和数组采取一些额外的安全措施,可以使这种方法变得安全,从而抵抗任何恶意JSON。

 

边界情况

然而,如果您现在对解决方案如此简单感到高兴,那么我有一些坏消息要告诉您:还有大量的边缘情况需要处理。只有一些是:

数组和数组元素(特别是在嵌套数组中) 多态性 抽象类和接口 ...

如果你不想摆弄所有这些(我打赌你不想),我很乐意推荐一个使用这种方法的概念验证的实验版本typejjson——我创建它就是为了解决这个问题,我自己每天都要面对的问题。

由于decorator仍被认为是实验性的,我不建议在生产中使用它,但到目前为止它对我来说很有用。