K7Buoy K7Buoy - 6 months ago 24
JSON Question

Angular Convert API JSON Response Object with Fields to Array

Learning Angular and having been through the heroes tutorial I wanted to try a real world http request so I am hitting the MetOffice DataPoint API. A JSON object is being returned that won't currently convert to an array for me to loop through. I am getting the following error:


Cannot read property 'data' of undefined


Having researched for a while now and tried a number of different suggestions from very similar questions, the closest being this one, below are the various things I have tried with comments on what failed at each attempt. I have confirmed in JSONLint that the JSON is valid.

Model

export class Site {
elevation: number;
id: number;
latitude: number;
longitude: number;
name: string;
region: string;
unitaryAuthArea: string;
}


Service

@Injectable()
export class MetOfficeService {
private testUrl = 'http://datapoint.metoffice.gov.uk/public/data/val/wxfcs/all/json/sitelist?key=xxx';

constructor(private http: Http) { }
getSites(): Observable<Site[]> {
return this.http.get(this.testUrl)
.map(this.extractData)
.catch(this.handleError);
}

private extractData(res: Response){
let body = res.json().Location; Cannot read property 'data' of undefined
//let body = res.json()['Location']; Cannot read property 'data' of undefined
//let temp = res.json()['Location'];
//let body = JSON.parse(temp); Unexpected token o on line 1 ... this is because of unrequired parsing i have discovered
//return body || { }; this is nonsense.
return body.data || { };
}

private handleError (error: Response | any) {
// In a real world app, you might use a remote logging infrastructure
let errMsg: string;
if (error instanceof Response) {
const body = error.json() || '';
const err = body.error || JSON.stringify(body);
errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
} else {
errMsg = error.message ? error.message : error.toString();
}
console.error(errMsg);
return Observable.throw(errMsg);
}
}


Component

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
providers: [MetOfficeService]
})
export class AppComponent implements OnInit {
title = 'test!';
errorMessage: string;
sites: Site[];

ngOnInit() { this.getSites(); }

constructor(private moService: MetOfficeService) { }
getSites(){
this.moService.getSites()
.subscribe(
sites => this.sites = sites,
error => this.errorMessage = <any>error);
}
};


View

<h1>
{{title}}
</h1>

<div id="sites">
<ul>
<li *ngFor="let site of sites">
{{site.name}} ||| {{site.unitaryAuthArea}}
</li>
</ul>
</div>


As always, all help greatly appreciated.

EDIT: A snippet of the JSON, it has 4,000 location objects or so.

{
"Locations": {
"Location": [{
"elevation": "7.0",
"id": "3066",
"latitude": "57.6494",
"longitude": "-3.5606",
"name": "Kinloss",
"region": "gr",
"unitaryAuthArea": "Moray"
}, {
"elevation": "6.0",
"id": "3068",
"latitude": "57.712",
"longitude": "-3.322",
"obsSource": "LNDSYN",
"name": "Lossiemouth",
"region": "gr",
"unitaryAuthArea": "Moray"
}, {
"elevation": "36.0",
"id": "3075",
"latitude": "58.454",
"longitude": "-3.089",
"obsSource": "LNDSYN",
"name": "Wick John O Groats Airport",
"region": "he",
"unitaryAuthArea": "Highland"
}]
}
}


Update Developer Tools showing response

enter image description here

Answer Source

There is no res.json().Location but res.json().Locations (note the "s") and furthermore, there is no object data that you are trying to extract from the response, but Location. So if you want to extract the array, your extractData should look like this:

private extractData(res: Response){
    let body = res.json().Locations.Location;  
    return body || [];
}