Json to Object deeply - json

In my TypeScript code I have 2 classes ClassA and ClassB:
export class ClassA {
name: string;
classB: ClassB;
getName(): string {
return this.name;
}
}
export class ClassB {
name: string;
getName(): string {
return this.name;
}
}
I try to parse a Json into a ClassA instance like this:
let classA: ClassA = Object.assign(new ClassA(), JSON.parse('{"name":"classA", "classB": {"name":"classB"}}'));
But only ClassA is instanciated, inside classB attribute is not.
I have the following result when I log objects:
console.log(classA); // ClassA {name: "classA", classB: Object}
console.log(classA.getName()); // classA
console.log(classA.classB); // Object {name: "classB"}
console.log(classA.classB.getName()); // EXCEPTION: classA.classB.getName is not a function
Is that possible to deeply parse a Json?

You'll need to do something like this:
export interface ClassBData {
name: string;
}
export interface ClassAData {
name: string;
classB: ClassBData;
}
export class ClassA {
name: string;
classB: ClassB;
constructor(json: ClassAData) {
this.name = json.name;
this.classB = Object.assign(new ClassB(), json.classB);
}
getName(): string {
return this.name;
}
}
Or you can instantiate ClassB the same way:
export class ClassB {
name: string;
constructor(json: ClassBData) {
this.name = json.name;
}
getName(): string {
return this.name;
}
}

So far, the best way I found is to do:
export class ClassA {
name: string;
classB: ClassB;
constructor(json: any) {
this.name = json.name;
if (json.classB !== undefined && json.classB !== null) {
this.classB = new ClassB(json.classB);
}
}
}
export class ClassB {
name: string;
constructor(json: any) {
this.name = json.name;
}
}

Related

Angular & RxJs : How to map json to Object Array

I try to map a Json http request answer to an object array (User) using RxJs :
My Json data looks like :
{"#context":"\/cotabe\/api\/contexts\/User","#id":"\/cotabe\/api\/users","#type":"hydra:Collection","hydra:member":[{"#id":"\/cotabe\/api\/users\/1","#type":"User","id":1,"email":"a.a#gmail.com","firstname":"Aaa","lastname":"Ggg","phone":"0606060606","mobile":"0606060607","fullName":"Aaa Ggg","username":"a.a#gmail.com","roles":["ROLE_DEVROOT","ROLE_USER"],"password":"$argon2i","createdAt":"-0001-11-30T00:00:00+01:00","updatedAt":"-0001-11-30T00:00:00+01:00","deleted":false}],"hydra:totalItems":1}
I would like to extract from that a User[], with user model :
export class User {
constructor(
public id: number,
public email: string,
public firstname: string,
public lastname: string,
public phone: string,
public mobile: string,
public roles: string[],
) {}
}
In my user service I have :
export class UserService {
private users: User[] = [];
userSubject = new Subject<User[]>();
constructor(private apiService: ApiService) { }
emitUsers() {
this.userSubject.next(this.users.slice());
}
getUsersFromRest() {
this.apiService.getEntriesFromRest('users').subscribe(
(data: User[])=>{
this.users = data['hydra:member'];
});
this.emitUsers();
}
}
with in an api service
public getEntriesFromRest (option: string): any {
return this.httpClient.get<any[]>(this.baseEndpoint + option);
}
I know it is an rXjs operator stuff, but I did not manage to find the solution.
Thank you for your help,
export class UserService {
userSubject = new Subject<User[]>();
userSubject$ = this.userSubject.asObservable();
constructor(private apiService: ApiService) {}
getUsersFromRest() {
this.apiService
.getEntriesFromRest("users")
.pipe(
map(x => JSON.stringify(x)),
map(x => JSON.parse(x)),
pluck("hydra:member")
)
.subscribe((data: User[]) => {
this.usersSubject.next(data);
});
}
}
Can you try the above code
export class UserService {
private userSubject = new Subject<User[]>();
userSubject$ = this.userSubject.asObservable(); // If you add a public observable of your subject, you can have other components subscribe to this, and not be able to alter the subject, but still get the data.
constructor(private apiService: ApiService) { }
getUsersFromRest() {
this.apiService.getEntriesFromRest('users')
.pipe(
map((x: any) => JSON.parse(x)) // Convert your response from JSON to an Object
.subscribe(
(data: User[]) => {
this.usersSubject.next(data.hydra.member);
});
}
}
There is no need to have a separate emit users method.

Is there any way to cast Json Object in angular

Able to receive response from server as a JSON object.Tried to cast JSON object to emp (type Employee),but not happening. What is the problem with my code?? Is there another way to solve this??
app.component.ts
export class AppComponent implements OnInit{
title = 'httpClientProject';
posts : JsonGet[];
emp : Employee;
constructor(private appService : AppService,private employeeService : EmployeeService){}
ngOnInit(){
this.getData();
this.getEmployee();
console.log(this.emp) }
getData() : void{
this.appService.getData().subscribe(posts=>(this.posts = posts)); }
getEmployee() : void{
this.employeeService.getEmployee().subscribe(data=>this.emp={...data}); } }
employee.service.ts
export class EmployeeService {
private baseUrl = 'http://localhost:8080/SpringRestWebService/getSingleEmployye';
constructor(private http: HttpClient) { }
getEmployee(): Observable<Employee> {
return this.http.get<Employee>(this.baseUrl); } }
Employee.ts
export class Employee {
public id: number;
public name: string;
public city: string;
constructor(id:number, name:string, status:string) {
this.id = id;
this.name = name;
this.city = status; } }
Json Response From Server
{
"id": 1,
"name": "asdasd",
"city": "Hasdasdayd"
}
Just define type when passing variable in subscribe
getEmployee() : void{
this.employeeService.getEmployee().subscribe((data:Employee)=>{
this.emp=data;
}
}

Convert Typescript class to string, with getter and setter instead of private variables

I have the following Typescript code:
class Foo {
private _id: number;
private _desc: string;
constructor(id: number, desc: string) {
this._id = id;
this._desc = desc;
}
public get id(): number {
return this.id;
}
public set id(value: number) {
this._id = value;
}
public get desc():string {
return this.desc;
}
public set desc(value: string) {
this._desc = value;
}
}
let foo = new Foo(1, 'something');
I would like to get a string from a Typescript class, I mean with getter and setter. Then I should get the following string:
{"id":1,"desc":"something"}
According to this answer I can reach that adding the following method to the class:
public toJSONString(): string {
return JSON.stringify(this, Object.keys(this.constructor.prototype));
}
It works.
It doesn't work if the Typescript class contains any other sub class.
So if I have the following code:
class Foo {
private _id: number;
private _desc: string;
private _user: Bar;
constructor(id: number, desc: string, user: Bar) {
this._id = id;
this._desc = desc;
this._user = user;
}
public get id(): number {
return this._id;
}
public set id(value: number) {
this._id = value;
}
public get desc():string {
return this._desc;
}
public set desc(value: string) {
this._desc = value;
}
public get user(): Bar {
return this._user;
}
public set user(value: Bar) {
this._user = value;
}
public toJSONString(): string {
return JSON.stringify(this, Object.keys(this.constructor.prototype));
}
}
class Bar {
private _name: string;
private _surname: string;
constructor(name: string, surname: string) {
this._name = name;
this._surname = surname;
}
public get name(): string {
return this._name;
}
public set name(value: string) {
this._name = value;
}
public get surname():string {
return this._surname;
}
public set surname(value: string) {
this._surname = value;
}
}
let foo = new Foo(1, 'something', new Bar('foo', 'bar'));
If I use toJSONString method I get the following string:
{"id":1,"desc":"something","user":{}}
instead of this:
{"id":1,"desc":"something","user":{ "name": "foo", "surname": "bar"}}
So, how can I get a string from a Typescript class that has other sub classes?
(If you need here is the playground for the first code and here is the playground for the second code)
There are 2 things to keep in mind here:
When you define getters and setters they do not become instance methods once transpiled into Javascript, but they are added to the prototype using Object.defineProperty. This means you won't get them simply using JSON.stringify
Passing the replacer array to JSON.stringify and telling it to use only the prototype values does the job but it kinda doesn't work on nested objects. Truth is JSON.stringify will parse only the properties with that name no matter where it resides in the object structure.
For Example
let a = {
user: "Foo",
data: {
name: "Bar"
}
};
JSON.stringify(a, ["user", "data"]);
Will output {"user":"Foo","data":{}} because even though the nested object's key is data, the object itself does not have properties named user or data
But
let a = {
user: "Foo",
data: {
user: "Bar"
}
};
JSON.stringify(a, ["user", "data"]);
Will output {"user":"Foo","data":{"user":"Bar"}} because the nested object has a proprety called user, just like its parent
I reckon this behavior can be confusing but it's possible to implement a solution by creating a method that gets all the properties of all the objects you are interested in. I haven't found a way in Typescript to check whether a class implements a interface (or extends a class) so I had to work a bit with what I know it works even though it's not that "elegant".
abstract class Stringifyiable {
private isStringifyiable(value): boolean {
return value != null && (typeof value === 'object' || typeof value === 'function') && value['getJsonKeys'] && typeof value['getJsonKeys'] === 'function';
}
public getJsonKeys(): string[] {
let keys = Object.keys(this.constructor.prototype);
keys.forEach(key => {
if (this.isStringifyiable(this[key])) {
keys = keys.concat(this[key].getJsonKeys());
}
});
return keys;
}
public toJSONString(): string {
return JSON.stringify(this, this.getJsonKeys());
}
}
class Foo extends Stringifyiable {
private _id: number;
private _desc: string;
private _user: Bar;
constructor(id: number, desc: string, user: Bar) {
super();
this._id = id;
this._desc = desc;
this._user = user;
}
public get id(): number {
return this._id;
}
public set id(value: number) {
this._id = value;
}
public get desc():string {
return this._desc;
}
public set desc(value: string) {
this._desc = value;
}
public get user(): Bar {
return this._user;
}
public set user(value: Bar) {
this._user = value;
}
}
class Bar extends Stringifyiable {
private _name: string;
private _surname: string;
constructor(name: string, surname: string) {
super();
this._name = name;
this._surname = surname;
}
public get name(): string {
return this._name;
}
public set name(value: string) {
this._name = value;
}
public get surname():string {
return this._surname;
}
public set surname(value: string) {
this._surname = value;
}
}
let foo = new Foo(1, 'something', new Bar('foo', 'bar'));
//this will output {"id":1,"desc":"something","user":{"name":"foo","surname":"bar"}}
foo.toJSONString();
Be careful with cyclic references because it will go into an endless loop (I'm sure it can be fixed though).

How to map an angular 2 class from an http call

I'm new in Angular.
I've a class called User:
export class User {
private id: number;
private name: string;
private surname: string;
get Id(): number {
return this.id;
}
set Id(newId: number) {
this.id = newId;
}
get Name(): string {
return this.name;
}
set Name(newName: string) {
this.name = newName;
}
get Surname(): string {
return this.surname;
}
set Surname(newSurname: string) {
this.surname = newSurname;
}
}
...a function to retrive an array of user:
getValues() {
this.usersService.getUsers()
.subscribe((users: User[]) => this.dataSource = users);
}
and a method to retrive the users array from backend WebApi:
getUsers(): Observable<User[]> {
return this.http.get<User[]>(this.usersSearchUrl)
.pipe(
tap(users => this.log(`fetched users`)),
catchError(this.handleError('getUsers', []))
);
}
finally the json returned from the webapi:
[{"id":"1","name":"Alberico","surname":"Gauss"},{"id":"2","name":"Anassimandro","surname":"Dirac"},{"id":"3","name":"Antongiulio","surname":"Poisson"}]
I would have expected that the call would automatically mapped the User class, instead it only gives me an array of type User, in fact if I write something in my component .subscribe((utenti: Utente[]) => console.log(utenti[0].Surname)); the console writes me "undefined". Can you tell me where I'm wrong? Thanks
You are retrieving JSON from your backend, as is expected. A Javascript (or typescript) class is not the same thing.
When the JSON is returned, it can be automatically converted into a simple JSON object in Javascript but it will NOT include all your getters and setters. So these class methods are not available, which is why you get undefined.
Remove all the getters and setters and add a constructor. Then you can just call Surname directly as a property and it will return the value (since it will then just be a plain JSON object).
export class User {
constructor() {
}
public id: number;
public name: string;
public surname: string;
}
Or without a constructor, and just declare the properties directly:
export class User {
public id: number;
public name: string;
public surname: string;
}
Or you could also use an interface:
export interface User {
id: number;
name: string;
surname: string;
}
You can read more about this issue here and here.
I think in component ts use like this code:
users: User[];
constructor(
private us: usersService,
public auths: AuthService
)
this.us.getUsers.subscribe(
users=> {
this.users= users.map((user) => {
return new User(user);
});
}
);
In service I think to write:
public getUsers(): Observable<User[]> {
let headers = new Headers();
headers.append('x-access-token', this.auth.getCurrentUser().token);
return this.http.get(Api.getUrl(Api.URLS.getUsers), {
headers: headers
})
.map((response: Response) => {
let res = response.json();
if (res.StatusCode === 1) {
this.auth.logout();
} else {
return res.StatusDescription.map(user=> {
return new User(user);
});
}
});
}
For me this logic work perfect. I hope to help you with this code

object destructuring on class members

Using ES6, I have a class in which I'm defining some variables and a function that will take an object and assing my variables to the values of it. This is repetitive, so is there any way I can use destructuring assingment to achieve this?
class BasicDataMgmt {
public id: number;
public name: string;
public email: string;
public phone: string;
public type: string;
fetchData(data) {
this.id = data.id;
this.name = data.name;
this.email = data.email;
this.phone = data.phone;
this.type = data.type;
}
}
It can be
fetchData(data) {
Object.assign(this, data);
}
for unsanitized data. Or
fetchData({ id, name, ... }) {
Object.assign(this, { id, name, ... });
}
for sanitized data.
Using Lodash _.pick is beneficial here for doing Object.assign(this, _.pick(data, ['id', 'name', ...])).