AdrienTorris
AdrienTorris

Reputation: 9391

Parse complex json objects with TypeScript

How can I parse complex json object with TypeScipt ?

I have a customer object, who have some invoices.

This is my model :

export class Customer {
    public id: string;
    public name: string;
    public invoices: CustomerInvoice[];

    get invoicesCount(): number {
        if (this.invoices== null) {
            return 0;
        }
        return this.invoices.length;
    }

     constructor() {
     }
}

export class CustomerInvoice {
     public id: number;

     constructor() {
     }
}

And in my service I have :

ngOnInit() {
    if (this.id != null) {
        this.dataService.getCustomer(this.id).subscribe(data => {
            this.customer = data;
        },
            err => console.log(err));
    }
}

Customer data are great (my customer id, name etc have some values) but the invoices are null.

The json is correct, data.Invoices.length return a number.

Upvotes: 12

Views: 15582

Answers (2)

kor_
kor_

Reputation: 1530

You could use the rxjs's map operator to manually map the entities in you DataService.

I haven't tested the code, so it may be that you'll need to map the invoices as well with rx (invoices.map( ... )) to iterate the collection, but the principle remains the same.

getCustomer(id: number): Observable<Customer> {
    return this.http.get<Customer>(this.customerUrl).map(customer => {
        let newCustomer = customer;

        newCustomer.invoices = customer.invoices;

        return newCustomer;
    });
}

Upvotes: 0

John Weisz
John Weisz

Reputation: 32022

How can I parse complex json object with TypeScipt ?

Assuming you mean parsing JSON into actual class instances instead of simple Javascript objects, TypeScript does not ship this feature off-the-shelf.

You may create an interface declaration using which you can do a type-assertion (not a type-cast) to somewhat mimick type-safety if the JSON is trusted, but that's it -- I know no native tool to serialize a JSON to actual instances of user-defined types.

interface ICustomerInvoice {
    id: number;
}

interface ICustomer {
    id: string;
    name: string;
    invoices: ICustomerInvoice[];
}

var customer: ICustomer = JSON.parse(json) as ICustomer;

Nevertheless, for the same obvious reasons I began putting together TypedJSON to introduce this feature into TypeScript. You can annotate your classes and members with JsonObject and JsonMember decorators:

@JsonObject
export class CustomerInvoice {
    @JsonMember
    public id: number;
}

@JsonObject
export class Customer {
    @JsonMember
    public id: string;

    @JsonMember
    public name: string;

    @JsonMember({ elementType: CustomerInvoice })
    public invoices: CustomerInvoice[];

    get invoicesCount(): number {
        if (this.invoices== null) {
            return 0;
        }
        return this.invoices.length;
    }
}

To deserialize a JSON-string, you would use TypedJSON.parse instead of JSON.parse, the getter will also be present as expected:

var customer = TypedJSON.parse(json, Customer);
typeof customer.invoicesCount; // "number"    

It is recommended to be used with ReflectDecorators (but not required). If you choose to skip this recommendation, you also need to specify the 'type' setting for members, for example:

@JsonMember({ type: String })
public id: string;

Upvotes: 11

Related Questions