简体   繁体   English

将 JSON 优化为 Typescript Map

[英]Optimise JSON to Typescript Map

I'm passing an object from Java to Typescript that has "Map" properties.我将 object 从 Java 传递到 Typescript 具有“地图”属性。 My typescript models are described by interfaces, not classes:我的 typescript 模型由接口描述,而不是类:

export interface Foo {
    bar: Map<string, Bar>;
    mapOfBar: Map<string, Map<string, Bar>>;
}

export interface Bar {
    someField: string;
}

I wrote code to query and convert the JSON object to Foo:我编写了代码来查询并将 JSON object 转换为 Foo:

import {HttpClient} from '@angular/common/http';
...
export class FooService {
    constructor(private http: HttpClient) {
    }

    getFoo(): Observable<Foo> {
        return this.http.get<Foo>('/some/route/to/FooEndpoint');
    }    
}

This returns me the object, but bar and mapOfBar are of type object, not Map .这返回了 object,但barmapOfBar的类型是 object,而不是Map So after quite a bit of searching the internet I came up with this code:所以在网上搜索了很多之后,我想出了这个代码:

    getFoo(): Observable<Foo> {
        return this.http
            .get<Foo>('/some/route/to/FooEndpoint')
            .pipe(
                map((res: Foo) => {
                    res.bar = new Map(Object.entries(res.bar));

                    let actualMapOfBar = new Map(Object.entries(res.mapOfBar));
                    res.mapOfBar = new Map();
                    for (let [key, bar] of actualMapOfBar) {
                        res.mapOfBar.set(key, new Map(Object.entries(bar)));
                    }

                    return res;
                })
            );
    }

This returns me bar and mapOfBar as Maps, which is great.这会将barmapOfBar作为 Maps 返回,这很棒。

Call me a perfectionist, but somehow this feels clunky and wrong:称我为完美主义者,但不知何故,这感觉很笨拙和错误:

  • The line map((res: Foo) => { indicates that res is already implementing the Foo interface, but it doesn't - it only contains the properties, but not the correct types (yet).map((res: Foo) => {表示res已经实现了Foo接口,但它没有 - 它只包含属性,但不包含正确的类型(还)。
  • This is quite a bit of conversion logic to set up, especially when dealing with deeply nested object trees.这需要设置相当多的转换逻辑,尤其是在处理深度嵌套的 object 树时。

Is there no easier way to implement this or can this be optimised?有没有更简单的方法来实现这个或者可以优化? Is there ideally some "automagical" way of using the Typescript interface to convert the object?理想情况下,是否有某种“自动”方式使用 Typescript 接口转换 object?

(Angular 9.1, Typescript 3.8, rxjs 6.5) (角度 9.1,Typescript 3.8,rxjs 6.5)

EDIT: This is an example response return by the endpoint:编辑:这是端点返回的示例响应:

{"bar":{"key1":{"someField":"A"},"key2":{"someField":"B"},"key3":{"someField":"C"}},"mapOfBar":{"A":{"key1":{"someField":"A"},"key2":{"someField":"B"},"key3":{"someField":"C"}},"B":{"key1":{"someField":"A"},"key2":{"someField":"B"},"key3":{"someField":"C"}},"C":{"key1":{"someField":"A"},"key2":{"someField":"B"},"key3":{"someField":"C"}}}}

No there is not.不,那里没有。 The line map((res: Foo) is -you- telling the compiler: "hey, this is of type 'Foo'", and the compiler accepts this, because he trusts you. However, you lied to the compiler, because the response of the API is just a plain JSON object. This cannot includes Map or any other class.线map((res: Foo)是 - 你 - 告诉编译器:“嘿,这是 'Foo' 类型”,编译器接受这个,因为他信任你。然而,你对编译器撒了谎,因为response of the API is just a plain JSON object. This cannot includes Map or any other class.

Like I said before, there is no automatic way to do the conversion.就像我之前说的,没有自动的方法来进行转换。 You will have to do this yourself.你必须自己做这件事。 Which means, to make it type safe, you should have two interfaces.这意味着,为了使其类型安全,您应该有两个接口。 One being the FooResponse and one into which you want this Foo to be converted to.一个是FooResponse ,另一个是您希望将此Foo转换为的。 The reason this does not exist, is because TypeScript types only exist at compile time.这不存在的原因是因为TypeScript类型仅在编译时存在。 During runtime, so when you digest the API, the types are lost, and so is your information for conversion.在运行时,因此当您消化 API 时,类型会丢失,您的转换信息也会丢失。 That's why you have to do it manually.这就是为什么您必须手动执行此操作的原因。

A small question.一个小问题。 Why do you want them to be maps?为什么要它们成为地图? Besides some relatively convenient API, you can also just keep it as the object it is, and change your typing to:除了一些比较方便的API,你也可以保持原样object,把你的打字改成:

export interface Foo {
    bar: Record<string, Bar>;
    mapOfBar: Record<string, Record<string, Bar>>;
}

export interface Bar {
    someField: string;
}

Is there no easier way to implement this or can this be optimised?有没有更简单的方法来实现这个或者可以优化? Is there ideally some "automagical" way of using the Typescript interface to convert the object?理想情况下,是否有某种“自动”方式使用 Typescript 接口转换 object?

Yes, it is!是的! It's called tapi.js - it's a lightweight automapper to/from JSON data.它被称为tapi.js - 它是一个轻量级的自动映射器,可以往返于 JSON 数据。

npm i -D tapi.js

Then you can map your object in a number of ways, namely:然后您可以通过多种方式 map 您的 object,即:

let typedObject = new YourClass().fromJSON(jsonData);

Or, more simply, you can map the contents of a promise like so:或者,更简单地说,您可以像这样map 的 promise 的内容

http.YOUR_REQUEST_CODE_HERE
    .as(YourClass)
    .[then/pipe/whatever](typedObject => { ... })

You can read the docs for more info.您可以阅读文档以获取更多信息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM