Ryan Ryan - 23 days ago 12
TypeScript Question

Including json schema files in typescript

I am writing an Angular 2 app with Typescript using the angular-cli tool.
I have a bunch of JSON schemas for my back-end API that I want to reference in my typescript so I can pass those schemas to a schema validator. What is the best way to include the .json files so I can reference them as a variable in my typescript and also have them bundled properly when I do a build with angular-cli?

My first thought was to make a custom module and export each schema as a const. I am not sure if that's possible and have been unable to find any example syntax on how to reference the files.

Here is my example code that works now. The only problem is I had to copy the schema file contents into my typesrcipt. I want to be able to reference the original schema file.

import { Injectable } from "@angular/core";
import { Http, Response } from "@angular/http";

import { Observable } from "rxjs/Observable";
import { WlanStatus } from "./wlan-status";
import { JsonValidatorService } from "../../json-validator.service";

//import WlanStatusSchema from "../../../api/app/plugins/wifi/schemas/getStatus.json";
const wlanStatusSchema =
{
"type": "object",
"properties": {
"result": {
"type": "object",
"properties": {
"cardState": {
"type": "integer"
},
"profileName": {
"type": "string"
},
"clientMAC": {
"type": "string"
},
"clientIP": {
"type": "string",
"format": "ipv4"
},
"clientName": {
"type": "string"
},
"AP_MAC": {
"type": "string"
},
"AP_IP": {
"type": "string"
},
"APName": {
"type": "string"
},
"Channel": {
"type": "integer"
},
"RSSI": {
"type": "integer"
},
"bitRate": {
"type": "integer"
},
"txPower": {
"type": "integer"
},
"DTIM": {
"type": "integer"
},
"beaconPeriod": {
"type": "integer"
},
"SSID": {
"type": "string"
},
"currentRadioMode": {
"type": "integer"
}
},
"required": [
"cardState",
"profileName",
"clientMAC",
"clientIP",
"clientName",
"AP_MAC",
"AP_IP",
"APName",
"Channel",
"RSSI",
"bitRate",
"txPower",
"DTIM",
"beaconPeriod",
"SSID",
"currentRadioMode"
]
}
},
"required": [
"result"
]
};

@Injectable()
export class WlanService
{
private getStatusUrl = 'app/getWlanStatus'; // URL to web API

constructor(private http: Http, private validator: JsonValidatorService) { }

scan(): void
{
console.log("Scanning for APs...")
}

getStatus(): Observable<WlanStatus>
{
return this.issueRequest(this.getStatusUrl, wlanStatusSchema);
}

private issueRequest(requestUrl: string, schema: any): Observable<Object>
{
return this.http.get(requestUrl)
.map((res: Response) =>
{
let body = res.json();
let valid = this.validator.validate(schema, body.data);
if (!valid)
{
throw (new TypeError("Not a valid response: " + JSON.stringify(this.validator.getErrors())));
}

return body.data.result;
})
.catch(this.handleError);
}

private handleError(error: Response | any)
{
// In a real world app, we 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);
}
}

Answer

Because I am using angular-cli and it uses webpack, I was able to solve my issue by using the require() function. Webpack will properly bundle the .json file this way.

I created a typescript file to import all my schemas, wlan-schema.ts:

export const WlanStatusSchema = require("../../../../api/app/plugins/wifi/schemas/getStatus.json");

to use it in my wlan.service.ts:

import * as schema from "./wlan-schema";

this.validator.validate(schema.WlanStatusSchema, apiResp);