0

This question is maybe not 100% original but the questions/answers I've found so far seem to neglect a certain elephant in the room.

I have a situation which I'll simplify for example's sake. Let's say I have the following JSON response:

[
  {
    "place": {
      "name": "The Cottage",
      "rating": 4
    }
  },
  {
    "place": {
      "name": "El Burrito Loco",
      "rating": 5
    }
  }
]

Then let's say I want to have a Typescript class called Place. If I instantiate each element as a Place, I have this stupid situation:

<!-- this is an Angular template -->

<ul>
  <li *ngFor="let place of places">{{ place.place.name }}</li>
</ul>

What I would prefer, of course, is {{ place.name }} rather than {{ place.place.name }}.

So my question is: how can I transform a JSON response to match my data model?

I've already seen questions/answers like this one.

What confuses me is that a) I imagine that this transformation desire is an incredibly common one, and b) all the answers I've found so far seem to involve a ton of hacky boilerplate (no offense).

So what's the deal, does everybody just copy and paste all this boilerplate into their apps to meet what must be a very common need, and the TypeScript (or Angular) developers just didn't come up with a way to meet this need with the language or framework? That doesn't seem likely. It's very confusing.

Any insight is very much appreciated.

Community
  • 1
  • 1
Jason Swett
  • 43,526
  • 67
  • 220
  • 351
  • Possible duplicate of [How do I cast a JSON object to a typescript class](http://stackoverflow.com/questions/22875636/how-do-i-cast-a-json-object-to-a-typescript-class), please see this [answer](http://stackoverflow.com/questions/22885995/how-do-i-initialize-a-typescript-object-with-a-json-object/22886730#22886730) for several possible solutions. – Jakub Synowiec Jan 31 '17 at 10:50

3 Answers3

1

I can think of simple map function over API call observable, which will create desired object structure in Place object model format.

this.dataService.getPlaces()
  .map(data => data.json())
  .map(items => items.map(
    item => <Place>({item.place.name, item.place})
  )
);
Pankaj Parkar
  • 134,766
  • 23
  • 234
  • 299
  • Ah, I can see how this would work, although there are some other properties (which I didn't include in my example) that I need to preserve. – Jason Swett Nov 20 '16 at 20:33
  • @JasonSwett How often this is going to happen.. for few objects writing your own custom map would make sense. – Pankaj Parkar Nov 20 '16 at 20:40
  • This particular transformation would presumably only need to happen in one or two places. Transformations in general might need to happen on any number of resources. – Jason Swett Nov 20 '16 at 20:42
  • @JasonSwett for one or two places having custom `map` would make sense for me. Going forward you should avoid such object format which are receiving from API, this implication will ensure that this kind of custom mapper will introduce again in application. – Pankaj Parkar Nov 20 '16 at 20:45
  • I ended up doing something different from what you suggested but I upvoted your answer because it helped get me on the right track. – Jason Swett Nov 20 '16 at 21:33
  • @JasonSwett thanks for letting me know, look at updated, I took advantage of es6 and made it more smaller and readable :) THanks ;) – Pankaj Parkar Nov 20 '16 at 21:43
  • Your latest update wouldn't work, unfortunately. The object doesn't have any keys and `item` is undefined. – Jason Swett Nov 23 '16 at 19:42
  • @JasonSwett sorry, I wasn't aware of your object structure, I corrected it now :) – Pankaj Parkar Nov 23 '16 at 19:48
0

JSON.parse actually supports an optional reviver argument that allows you to control the creation of any given property during the deserialization process. You should be able to use that to create an instance of your class.

Richard Szalay
  • 83,269
  • 19
  • 178
  • 237
0

Here's what I ended up doing. Thanks to Pankaj Parkar for providing some clues that led me here.

  getList(): Observable<Place[]> {
    return this.http.get(this.placesUrl)
               .map(response => response.json().data)
               .map(data => data.map((item) => {
                 return <Place>({
                   name: item.place.name,
                   place: item.place
                 });
               }));
  }
Jason Swett
  • 43,526
  • 67
  • 220
  • 351