Typescript JSON string to class - json

Let be this JSON string:
[
{
"id": 1,
"text": "Jon Doe"
},
{
"id": 1,
"text": "Pablo Escobar"
}
]
Let be this class:
export class MyObject{
id: number;
text: string;
}
How can I cast this JSON string to list of MyObject?
If I do:
console.log(<MyObject[]>JSON.parse(json_string));
It returns a list of Object instead of a list of MyObject

You don't necessarily need a class here. You can just use an interface
export interface MyObject{
id: number;
text: string;
}
Then you can just write:
var myObjArray : MyObject[] = [
{
"id": 1,
"text": "Jon Doe"
},
{
"id": 1,
"text": "Pablo Escobar"
}
];
If you data comes from the server, you will probably have it in a variable of type any, and you can just assign it to an array of that type and it will work as expected.
var data: any = getFromServer();
var myObjectArray:MyObject[] = data;
In typescript you don't need a class implementing an interface. Any object literal that satisfies the interface contract will do.
If your data is still in string for you can just use JSON.parse(jsonString) to parse the string to JavaScript objects.
See playground here

You will need to create a constructor for your class, and call it for each item in the list you receive.
export class MyObject{
constructor(public id: number, public text: string) { }
}
let data = [
{
"id": 1,
"text": "Jon Doe"
},
{
"id": 1,
"text": "Pablo Escobar"
}
];
let objects = data.map(o => new MyObject(o.id, o.text));
You can check it out in the playground here.

There is a problem when MyObject has 50 or more properties...
Add a constructor in your MyObject class so that it extends your json object.
export class MyObject {
constructor( json: any )
{
$.extend(this, json);
}
id : number;
text : string;
methodOnMyObject() {...}
}
In your ajax callback, create the MyObject object from your json Object:
let newObject = new MyObject( json );
newObject.methodOnMyObject();
I detailed the solution in that post.

One more way to achieve this:
var data: any = getFromServer();
var myObjectArray = data as MyObject;
Or:
var data: any = getFromServer();
var myObjectArray = <MyObject>dataMyObject;

Related

Transform json to class intstance with class-transformer

I would like to create an instance of the Customer class from Json object.
But using the plainToInstance function of class-transformer I don't have the proper class instance as a type save typescript object.
What I'm doing bad?
Import
import { plainToInstance } from 'class-transformer';
Customer JSON
const json = `{
"id": "1",
"name": "Jogn",
"surname": "Doe",
"email": "j.doe.test#gmail.com",
"phone": "123456789"
}
}
`;
Customer class definition
import { Field, ObjectType, Directive, ID } from '#nestjs/graphql';
import { Address } from './address';
#ObjectType()
#Directive('#key(fields: "id")')
export class Customer {
#Field(() => ID)
id: string;
#Field()
name: String;
#Field({nullable: true})
surname?: String;
#Field()
email: String;
#Field({nullable: true})
phone?: String;
#Field()
customerType: String;
#Field()
customerStatus: String;
#Field(() => [Address], { nullable: true })
addresses?: [Address]
}
Transformation from Json to Customer instance
let customer : Customer = plainToInstance(Customer, json) as Customer;
console.log('customer.email);
Console result
Customer email: undefined
So I couldn't get the email of the customer here
This is what I have when I log the entire customer variable
console.log(customer);
{
"id": "1",
"name": "Jogn",
"surname": "Doe",
"email": "j.doe.test#gmail.com",
"phone": "123456789"
}
Test with creating the Customer instance inline
var x = new Customer();
x.id = "123";
console.log(x)
So, now the object looks properly in the console
Customer { id: '123' }
You must pass a json object to plainToInstance - not a string.
i.e. your json variable should be
const json = {
id: '1',
name: 'Jogn',
surname: 'Doe',
email: 'j.doe.test#gmail.com',
phone: '123456789',
};
here's a working Stackblitz example
The second attribute of plainToInstance should be a plain object so you have to parse your json string into an object:
let customer = plainToInstance(Customer, JSON.parse(json))

How to map JSON response to Model in Angular 4

I have tried a lot but I am not able to get endpoint response mapped to my model. I am using HttpClient and Angular4.
I got data back from service but it is not mapped correctly to my model class.
I have following JSON which service is Returning:
{
"result": [
{
"id": "1",
"type": "User",
"contactinfo": {
"zipcode": 1111,
"name": "username"
}
}
]
}
I have created a model in typescript which I will like to map to json response:
export interface result {
zipcode: number;
name: string;
}
This is how i call JSON endpoint.
result : string[] = [];
constructor(private http: HttpClient) { }
public getList(): result[] {
this.http.get<result[]>('url...', { headers: this.headers }).subscribe(
data => {
// 1. Data is returned - Working
console.log('data: ' + data);
this.result= data;
// This is the problem. My data is not mapped to my model. If I do following a null is returned
console.log('data mapped: ' + this.result[0].name);
},
(err: HttpErrorResponse) => {
// log error
}
);
return this.result;
}
You need to import the interface in your component,
import { result } from '.result';
Your interface should look like,
interface RootObject {
result: Result[];
}
interface Result {
id: string;
type: string;
contactinfo: Contactinfo;
}
interface Contactinfo {
zipcode: number;
name: string;
}
and change the type of result as,
result : result;
and assign the result as,
this.result = data;
You can use http://www.jsontots.com/ to create the interface based on JSON
your "data" is an Object, with a property "result". result[] has a property called "contactInfo". in contactInfo you have the data you want, so
//you have no model, so NOT get<result[]>
this.http.get('url...', { headers: this.headers }).subscribe(
data => {
this.result= data.result[0].contactInfo;
}

Angular2 mapping of nested JSON arrays to model

I am trying to map the received JSON-data on my created Models. The problem is, that the JSON-data has nested arrays. So it is not possible to map my data with the way I am trying to. Is there a mistake in my way or is there a better way to map my JSON-data ?
JSON-Data
{
"data": {
"apiName": "test-application",
"stages": [
{
"stage": "prod",
"id": "xxxxxxxx",
"methods": [
{
"id": "xxxxxx",
"path": "/users/create",
"httpMethods": [
"GET"
],
"methodName": "testMethod",
"url": "https://xxxx/xxxxx/xxxxxx"
}
]
},
{
"stage": "dev",
"id": "xxxxxxx",
"methods": [
{
"id": "xxxxxxx",
"path": "/users/create",
"httpMethods": [
"GET"
],
"methodName": "testMethod",
"url": "https://xxxxx/xxxxxx/xxxx"
}
]
}
]
}
}
Models:
import {ApiStage} from "./ApiStage";
export class Api {
constructor(values: Object = {}){
Object.assign(this, values);
}
public apiName: string;
public stages: ApiStage[];
}
import {ApiMethod} from "./ApiMethod";
export class ApiStage {
constructor(values: Object = {}){
Object.assign(this, values);
}
public stage: string;
public id: string;
public methods: ApiMethod[];
}
export class ApiMethod {
constructor(values: Object = {}){
Object.assign(this, values);
}
public id: string;
public path: string;
public httpMethods: string[];
public methodName: string;
public url: string;
}
HTTP-method in service:
getApi() {
return this.http.post(this.url, this.data, {headers: this.headers})
.map(this.extractData)
.map(api => new Api(api))
.catch((error: any) => Observable.of(error.json().error || 'Server error'));
}
private extractData(res: Response) {
let body = res.json();
return body.data || {};
}
JSON has just a very limited set of data types - string, number, boolean, array, object. If you want to convert a JSON object tree to a tree of objects of your custom classes, it's necessary to do it recursively and with creating correct objects - not working with objects that just look like being of your classes.
This process can be tedious and error prone, so it's better to use a library such as Class transformer (https://github.com/pleerock/class-transformer) which can do it for you. You just annotate your classes with decorators (such as #Type(...)) and then you can transform plain JSON objects using plainToClass() method or serialize real objects to JSON using classToPlain().

Angular2 & Typescript - Create object from nested JSON

I have a class which has multiple interfaces inside of it to model a JSON data. For example:
interface A {
id: number;
}
interface B {
name: string;
surname?: string;
}
class MyClass implements A {
people: B[];
notes: string[];
function1(){...}
function2(){...}
}
And I have a JSON in the same structure:
{
id: 1,
people: [
{
name: "john"
},
{
name: "alice",
surname: "smith"
}
],
notes: [ "Very important top secret note" ]
}
Can I create an instance of MyClass from this JSON directly?
Your data structure is almost the same as your class, you'd have to add an id property to the class
class MyClass implements A {
id: number;
// ....
}
The problem is if you were trying to do something like this:
let data: MyClass = {
id: 1,
people: [
{
name: "john"
},
{
name: "alice",
surname: "smith"
}
],
notes: [ "Very important top secret note" ]
}
This won't work because your json does not have the methods (function1, function2).
One solution would be to really instantiate the MyClass and pass the json, or have a constructor method for that like
class MyClass {
static createFrom(jsonData: A & B): MyClass {
// create the objct and return
}
}
Or, you could create a variable of that type by combining an existing instance of the class and spreading the json.
Like so:
let json = {
id: 1,
people: [
{
name: "john"
},
{
name: "alice",
surname: "smith"
}
],
notes: ["Very important top secret note"]
}
const c = new MyClass();
let mClass: MyClass = {...json, function1: c.function1, function2: c.function2 };
mClass.function1();
Link to playground

Angular2 parsing from JSON to object

I'm trying to find the best way to cast my json object to Typescript object.
I have a http get service which returns a list of user.
My current version works, I have added from JSON function to all my model classes to make the mapping works:
export class User {
constructor(
public pk: number,
public username: string,
public first_name: string,
public last_name: string,
public email: string,
public profile: UserProfile, ) {
}
static fromJSON(json: any): User {
let user = Object.create(User.prototype);
Object.assign(user, json);
user.profile = UserProfile.fromJSON(json.profile);
return user;
}
}
That works well. But there is something I don't get in the angular 2 doc. On the heroes tutorial, the JSON is automatically casted to object this way:
getHeroes (): Observable<Hero[]> {
return this.http.get(this.heroesUrl)
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
let body = res.json();
return body.data || { };
}
I can't get this method to work on my case, I says that body.data is undefined.
Does this method really works?
EDIT:
My http service doesn't returns an array of users. It returns a page which contains an array of users in its 'results' property.
{
"count": 2,
"next": null,
"previous": null,
"results": [
{
"pk": 48,
"first_name": "Jon",
"last_name": "Does",
"profile": {
"pk": 46,
"gender": "U"
}
},
{
"pk": 47,
"first_name": "Pablo",
"last_name": "Escobar",
"profile": {
"pk": 45,
"gender": "M"
}
}
]
}
My service code:
private extractData(res: Response) {
let body = res.json().results;
return body || {}; //<--- not wrapped with data
}
search(authUser: AuthUser, terms: string): Observable<User[]> {
let headers = new Headers({
'Content-Type': 'application/json',
'X-CSRFToken': this.cookiesService.csrftoken,
'Authorization': `Token ${authUser.token}`
});
let options = new RequestOptions({ headers: headers });
return this.http.get(environment.server_url + 'user/?search=' + terms, options)
.map(this.extractData);
// .map((response: Response) => response.json());
}
My search component code:
onSearch(terms: string) {
this.searchService.search(this.user, terms).subscribe(
response => {
console.log(response); // Return array of object instead of array of user
},
error => {
console.log(JSON.stringify(error));
},
() => { }
);
}
EDIT 2:
To make this case easier, I've wrote this simple code:
test(){
let json_text=` [
{
"id": 1,
"text": "Jon Doe"
},
{
"id": 1,
"text": "Pablo Escobar"
}
]`;
console.log(<MyObject[]>JSON.parse(json_text)); // Array of objects
console.log(MyObject.fromJSON(JSON.parse(json_text))); // Array of 'MyObject'
}
export class MyObject{
id: number;
text: string;
static fromJSON(json: any): MyObject {
let object = Object.create(MyObject.prototype);
Object.assign(object, json);
return object;
}
}
console.log(<MyObject[]>JSON.parse(json_text)) returns a list of Objects
console.log(MyObject.fromJSON(JSON.parse(json_text))) returns a
list of MyObject
It's because in Angular tutorial, json is in the data property.
As stated in the tutorial
Make no assumptions about the server API. Not all servers return an
object with a data property.
If you are not wrapping your json with any property you can just use
private extractData(res: Response) {
let body = res.json();
return body || { }; //<--- not wrapped with data
}
Update:
Component code
onSearch(terms: string) {
this.searchService.search(this.user, terms).subscribe(
(response: SearchResponse) => { // <--- cast here
console.log(response);
},
error => {
console.log(JSON.stringify(error));
},
() => { }
);
}
I am quite late to this topic but found my self into the same issue . I am learning Angular and want to convert JSON received from HTTP server to my model object .
Service Class
var ele:User;
let k=this.http.get<User>(url).subscribe(data => {
ele=data;
console.log(ele.count);
console.log(ele.results[0].first_name);
console.log(ele.results[0].profile.gender);
}
);
My Model for holding the information of JSON
export interface User{
count: string;
next: string;
previous: string;
results: Result[];
}
export interface Result{
pk: string;
first_name: string;
last_name: string;
profile:Profile;
}
export interface Profile{
pk: string;
gender:string;
}
And this is it. I am using Angular 6 for parsing JSON to Object