在Angular api服务中安全地处理深度嵌套的复杂json对象

在Angular api服务中安全地处理深度嵌套的复杂json对象,angular,typescript,nested,rxjs,Angular,Typescript,Nested,Rxjs,我正在为carwash开发一个API服务,它检索一个大型、复杂的json对象(carwash对象)。此对象的几乎每个属性都是另一个对象,它包含简单数据类型和其他必须实例化的自定义对象的混合体。我已经看到了很多使用RxJs映射函数的具有1或2级属性的对象的示例,但是对于像这样的深度嵌套对象,这似乎不可行 Carwash.model.ts概述 export class Carwash { public name: string; public type: CARWASH_TYPE; public r

我正在为carwash开发一个API服务,它检索一个大型、复杂的json对象(carwash对象)。此对象的几乎每个属性都是另一个对象,它包含简单数据类型和其他必须实例化的自定义对象的混合体。我已经看到了很多使用RxJs映射函数的具有1或2级属性的对象的示例,但是对于像这样的深度嵌套对象,这似乎不可行

Carwash.model.ts概述

export class Carwash {
public name: string;
public type: CARWASH_TYPE;
public rating: Rating;
public address: Address;
private _coordinates: CarwashCoordinates;
public promotions: Promotion[];
public silverPackage: Package;
public goldPackage: Package;
public platinumPackage: Package;
public exteriorPackage: Package;
public interiorPackage: Package;
public completePackage: Package;
public storeHours: StoreHours;
}
ApiService.ts

public static carwashUrl = 'http://localhost:4200/assets/data/carwash.json';
private static carwashObject: Carwash;
public name: string;
public type: CARWASH_TYPE;
public ratings: Rating[];
public address: Address;
private _coordinates: CarwashCoordinates;
public promotions: Promotion[];
public silverPackage: Package;
public goldPackage: Package;
public platinumPackage: Package;
public exteriorPackage: Package;
public interiorPackage: Package;
public completePackage: Package;
public storeHours: StoreHours;
constructor(private http: HttpClient) {
}

public getCarwash() {
    console.log('getCarwash called');
    if (!CarwashService.carwashObject) {
        this.fetchCarwash().subscribe(
            (carwash => CarwashService.carwashObject = carwash)
        );
    } else {
        console.log('Carwash already fetched.');
    }
}

private fetchCarwash(): Observable<Carwash> {
    return this.http.get<any>(CarwashService.carwashUrl).pipe(
        map(res => {
            const carwashData = res.json();
            this.name = carwashData.name; // Top level stuff is np
            this.type = carwashData.type; // Top level stuff is np
            for (const rating of carwashData.ratings) {
                this.ratings.push(new Rating(rating.customerName,
                    rating.score,
                    rating.review,
                    rating.date)); // Top level stuff is np
            }
            this.address = // When it comes to instantiating custom objects I'm not sure how to go about it in an elegant way.
            return new Carwash(this.name, this.type, etc...)
        })
    )
}
publicstaticcarwashurl='1〕http://localhost:4200/assets/data/carwash.json';
私人静态洗车对象:洗车;
公共名称:字符串;
公共型:洗车型;
公众评级:评级[];
公共广播:广播;
专用坐标:洗车坐标;
公共宣传:宣传[];
公共包装:包装;
公共黄金套餐:套餐;
公共铂包装:包装;
公共外部包装:包装;
公共室内包装:包装;
公共completePackage:包;
公共存储时间:存储时间;
构造函数(专用http:HttpClient){
}
公共洗车服务(){
log('getCarwash调用');
if(!CarwashService.carwashObject){
此文件为.fetchCarwash().subscribe(
(carwash=>CarwashService.carwashObject=carwash)
);
}否则{
console.log('已获取洗车服务');
}
}
私有fetchCarwash():可观察{
返回这个.http.get(CarwashService.carwashUrl.pipe)(
地图(res=>{
const carwashData=res.json();
this.name=carwashData.name;//顶级内容是np
this.type=carwashData.type;//顶级内容是np
用于(洗车数据的常数额定值。额定值){
此.ratings.push(新评级)(Rating.customerName,
评分,
评级、审查,
rating.date);//顶级的东西是np
}
this.address=//当涉及到实例化自定义对象时,我不确定如何以优雅的方式进行。
返回新的洗车服务(this.name、this.type等)
})
)
}
映射是实现这一目标的唯一途径吗?我应该实现helper函数来清理get调用吗?我不知道


注意:不需要自定义对象结构。您只需知道它们由基本数据类型和其他自定义对象组成。

您可以执行以下操作:

http.get<CarWash>(this.carwashApiUrl).subscribe((carwash: CarWash) => console.log(carwash));
http.get(this.carwashApiUrl).subscribe((carwash:carwash)=>console.log(carwash));
。。。当然,您的CarWash类需要定义所有可用的复杂属性。对于此类定义,还可以使用生成器(如Swagger编辑器)将它们从api的类定义中删除(如果有的话)。
之后,您仍然可以将您的CarWashDto映射到一个CarWash对象,而不管您在UI中需要它。

如果您需要实际的对象实例,而不仅仅是一个具有适当类型的对象图,那么您可以使用如下代码:

使用对象。分配:

  productsClassInstance$ = this.http.get<ProductClass>(this.productsUrl)
    .pipe(
      map(product => Object.assign(new ProductClass(), {
        ...product,
        suppliers: (product.suppliers ? product.suppliers.map(supplier => Object.assign(new SupplierClass(), {
          ...supplier
        })) : [])
      }))
    );

[再次更新以使用构造函数而不是Object.assign]

也许Pulk是一种方法。检查这个。检查,还有flatMapDepth和flatMapDepth。我只是试着确认一下。。。它会自动适当地映射所有嵌套项。但请注意,它们的形状都像定义的类型。。。它们实际上不是这些类型的实例。如果您需要它们成为实例(例如调用方法),那么这种技术是不够的。对不起,我更新了我的问题以澄清。我知道这项技术,但需要实例化每个自定义对象,因为大多数自定义对象都包含自己的函数。有趣的是,我今天早上在AiA的一集中听到你在上班的路上回家看到你。关于主题+1.Hmm,这很有趣。你测试过这个吗?我实现了一个类似的解决方案,Object.assign函数不能与需要参数的类一起工作……在我的例子中,每个类都需要参数。您可以在这里找到它:在product.service中,属性:ProductsClassInstanceMultipleLevel$。请注意,我的类构造函数没有参数。乍一看,您可能需要更改类以定义无参数构造函数并使用扩展语法。或者使用构造函数并手动指定每个属性。仍然不能完全确定。构造函数解决方案本质上就是我试图避免的。我确实尝试过Object.assign,但代码长度/可读性基本上是相同的(这里的扩展运算符对我没有多大好处)。另外,在尝试使用类实现时,Object.assign方法是否不太老练?假设我以后需要使用构造函数以传统方式使用该类。JS/TS中没有重载,因此。。。
productsClassInstanceMultipleLevels2$ = this.http.get<ProductClass[]>(this.productsUrl)
.pipe(
  map(products => products.map(product => 
    new ProductClass(
      product.id, 
      product.productName,
      (product.suppliers ? product.suppliers.map(supplier => new SupplierClass(
        supplier.id,
        supplier.name
      )) : []) ))
  )
);