简体   繁体   中英

Angular2 mapping nested JSON to model - multiple Requests

I'm having problems mapping two HTTP API Responses into my Model in Angular 2. With one API Call I get something like this:

[{
    "id": 410,
    "name": "Test customdata test",
    "layer": [79,94]
}, {
    "id": 411,
    "name": "Test customdata test2",
    "layer": 79
}]

The other request can give me this

[{
    "id": 94,
    "name": "xy"
}, {
    "id": 79,
    "name": "abc"
}]

My model looks something like this:

export class Dataset {
 public id: number;
 public name: string;
 public layer: Layer[];
}
export class Layer {
 public id: number;
 public name: string;
}

how can I know map this JSONs into the one model. I would need to nested HTTP requests to get all the Data don't I? If I make it like this

this.Http.get('urltogetdataset').map(response => response.json() as Dataset[] )

I get my Dataset Objects but of course the layer parameter is an array of numbers. Any body having an idea for this? Thanks!

I hope you mean to have 2 seperate routes?

/api/datasets => gets you datasets
/api/layers => gets you layers

You can then proceed to map the responses to the classes like this:

http.get('/api/layers').map(response => {
    return response.json().map(item => new Layer().deserialize(item));
});

I recommend to implement some sort of deserialization in the classes like this:

export class Layer {
    public id: number;
    public name: string;

    public deserialize(input){
        this.id = input.id;
        this.name = input.name;
    }
}

Edit:

For the dataset do something like this:

http.get('/api/datasets').map(response => {
    let dataset = response.json().map(item => {
        let dataset = new Dataset().deserialize(item)
        for(let layerId of item.layer){
            dataset.addLayer(layerService.getLayerById(layerId);
        }
        return dataset;
    });
});

This will requires the Dataset class to have an addLayer method.

export class Dataset {
    public id: number;
    public name: string;
    public layer: Layer[];

    public deserialize(input){
        this.id = input.id;
        this.name = input.name;
    }

    public addLayer(layer){
        this.layer.push(layer);
    }
}

Additionally you will need something like a layerService to get the previously loaded layers by their ID's. This is an example of the getLayerById method:

public getLayerById(id){
    for(let layer of this.layers){
        if(layer.id === id) return layer;
    }
}

I suppose that your API return all items per resources. If not the case tell me.

So, I propose to you this solution:

export class RESTDatasetLoader {
  ...

  retrieveDataset(): Observable<Dataset[]> {    
    return Observable.of(this.jsonDataset) //<- remplace here by the call to datasets
      .combineLatest(Observable.of(this.jsonLayer), //<- remplace here by the call to layers
          (dataset, layers) => this.mapToDataset(dataset, layers)
      )
  }

  /*
   * with your question, we have the call to dataset resource so:
   * retrieveDataset(): Observable<Dataset[]> {    
   *     return this.Http.get('urltogetdataset')
   *        .combineLatest(Observable.of(this.jsonLayer), //<- We don't have this call in your question
   *           (dataset, layers) => this.mapToDataset(dataset, layers)
   *        )
   * }
   */


  private mapToDataset(dataset: DatasetDTO[], layers: LayerDTO[]): Dataset[] {
    return dataset.map(data => {
      const mappedLayers: Layer[] = this.mapLayers(data, layers)
      return new Dataset(data.id, data.name, mappedLayers)
    })
  }

  private mapLayers(dataset: DatasetDTO, layers: LayerDTO[]): Layer[] {
    return dataset.layer
      .map(layerId => {
        const layer = layers.find(layer => layer.id === layerId)
        return new Layer(layer.id, layer.name)
      })
  }
}

In your code you must reference RESTDatasetLoader in Providers of the module. And, inject it to your angular component

To have the full code: https://stackblitz.com/edit/angular-eoqebo?file=app%2Fapp.component.ts

In agree with @Aluan Haddad to cast the response, prefer an interface, it's a DTO (Data Transfer Object).

After this you map the response to your own object, because if the response of the API changes you just change at one place and not everywhere in your code.

Maybe, there is an error in your question when you have one layer into Dataset response it's not an array but just a number. If it's not an error, it's preferable to have always the same structure if you can to speak to API team about this point.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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