I am trying to consume a web API that returns the following data
{
"FileStatuses": {
"FileStatus": [
{
"accessTime": 0,
"blockSize": 0,
"childrenNum": 13,
"fileId": 16396,
"group": "supergroup",
"length": 0,
"modificationTime": 1553247533630,
"owner": "hduser",
"pathSuffix": "demo-data",
"permission": "755",
"replication": 0,
"storagePolicy": 0,
"type": "DIRECTORY"
},
{
"accessTime": 0,
"blockSize": 0,
"childrenNum": 7,
"fileId": 16410,
"group": "supergroup",
"length": 0,
"modificationTime": 1550659883380,
"owner": "hduser",
"pathSuffix": "instacart",
"permission": "755",
"replication": 0,
"storagePolicy": 0,
"type": "DIRECTORY"
}
]
}
}
I created a service like this and the class to parse the json response to it:
public getHadoopDirList(): Observable<FileStatus[]> {
return this.http.get<FileStatus[]>(this.webHdfsUrl, {}).pipe(map(data => data));
}
export class FileStatus {
accessTime: number;
blockSize: number;
childNum: number;
fileId: number;
group: string;
length: number;
modificationTime: number;
owner: string;
pathSuffix: string;
permission: string;
replication: number;
storagePolicy: number;
type: string;
}
i subscribed to it on the component but when i try to iterate over it on the template i get the following ERROR Error: Error trying to diff '[object Object]'. Only arrays and iterables are allowed
I think the problem is the way how to map it but I didn't know how to solve it
use http://json2ts.com/ to convert JSON to interface
Your inteface should be like below
export interface FileStatus {
accessTime: number;
blockSize: number;
childrenNum: number;
fileId: number;
group: string;
length: number;
modificationTime: any;
owner: string;
pathSuffix: string;
permission: string;
replication: number;
storagePolicy: number;
type: string;
}
export interface FileStatuses {
FileStatus: FileStatus[];
}
export interface FileStatusesRootObject {
FileStatuses: FileStatuses;
}
and then
return this.http.get<FileStatusesRootObject>(
You need to make sure the data types match. It expects a result of type FileStatus[]. Thus, on your RxJS's map(), you will need to return the right data respectively by selecting FileStatus, which contains the array of objects with the type of FileStatus
public getHadoopDirList(): Observable<FileStatus[]> {
return this.http.get<FileStatus[]>(this.webHdfsUrl, {})
.pipe(
map(data => data['FileStatuses']['FileStatus'])
);
}
Related
I have an problem which I can't resolve in any way, I'm including user -> avatar models in PostTip model and when I want to add column with OBJECT avatar (id + src) I can't make it.
Name, email works because I'm pointing to single value not object.
Where is the problem?
PostTip.findAndCountAll({
attributes: {
include: [
** [db.sequelize.col(`"user"->"avatar"`), "avatar"],**
[db.sequelize.col(`"user"."name"`), "name"],
[db.sequelize.col(`"user"."email"`), "email"],
],
},
offset: offset,
limit: limit,
order: order,
where: {
postId: req.params.id,
},
include: [
{
model: User,
as: "user",
attributes: [],
include: [{ model: AwsAvatar, as: "avatar" }],
},
],
}).then((result) => {
console.log(JSON.stringify(result.rows, null, 2));
const data = getPagingData(result.rows, result.count, query.page, limit);
return res.send(data);
});
};
and results are:
{
"id": 1,
"userId": 1,
"postId": 1,
"tipAmount": 100,
"createdAt": "2023-01-11T22:10:26.440Z",
"updatedAt": "2023-01-11T22:10:26.440Z",
** "avatar": "(1,1,https://dh7ieyc6s2dxm.cloudfront.net/avatars/1673514260216_0e69489d-fc95-4ed8-b615-2a76ce43385.webp,\"2023-01-12 09:04:21.223+00\",\"2023-01-12 09:04:21.223+00\")",**
"name": "Test User",
"email": "test#test"
}
Im expecting to have output like that:
{
"id": 1,
"userId": 1,
"postId": 1,
"tipAmount": 100,
"createdAt": "2023-01-11T22:10:26.440Z",
"updatedAt": "2023-01-11T22:10:26.440Z",
** "avatar": {
"id": 1,
"userId": 1,
"src": "https://dh7ieyc6s2dxm.cloudfront.net/avatars/1673514260216_0e69489d-fc95-4ed8-b615-2a76ce5ff385.webp",
"createdAt": "2023-01-12T09:04:21.223Z",
"updatedAt": "2023-01-12T09:04:21.223Z"
},**
"name": "Test User",
"email": "test#test"
}
User model:
import { Optional } from "sequelize";
import {
Column,
Table,
Model,
DataType,
ForeignKey,
BelongsTo,
Default,
HasMany,
HasOne,
} from "sequelize-typescript";
import UserRole from "../../enums/user-role.enum";
import AwsAvatar from "../aws/aws-avatar.model";
import PostComment from "../post/post-comment.model";
import PostLike from "../post/post-like.model";
import PostTip from "../post/post-tip.model";
import Card from "./card.model";
import Role from "./role.model";
interface UserAttributes {
id: number;
avatar: AwsAvatar;
name: string;
setName: string;
description: string;
hashtag: string;
email: string;
password: string;
isVip: boolean;
lastUsedCardNum: string;
sendNotifications: boolean;
cards: Card[];
}
interface UserCreationAttributes
extends Optional<
UserAttributes,
| "id"
| "avatar"
| "setName"
| "description"
| "hashtag"
| "cards"
| "isVip"
| "lastUsedCardNum"
| "sendNotifications"
> {}
#Table({ tableName: "users" })
export default class User extends Model<
UserAttributes,
UserCreationAttributes
> {
#Column(DataType.STRING)
public name!: string;
#Column(DataType.STRING)
public setName!: string;
#Column(DataType.STRING)
public email!: string;
#Column(DataType.STRING)
public password!: string;
#HasOne(() => AwsAvatar)
public avatar!: AwsAvatar;
#Column(DataType.STRING)
public description!: string;
#Column(DataType.STRING)
public hashtag!: string;
#Default(false)
#Column(DataType.BOOLEAN)
public isVip!: boolean;
#Default(false)
#Column(DataType.BOOLEAN)
public subscriptionActive!: boolean;
#Column(DataType.DATE)
public subscriptionExpireDate!: Date;
#Default(false)
#Column(DataType.BOOLEAN)
public emailConfirmed!: boolean;
#Default(false)
#Column(DataType.BOOLEAN)
public invalidEmail!: boolean;
#Column
public buyedContent!: string;
#Default(0)
#Column(DataType.INTEGER)
public wallet!: number;
#Default(0)
#Column(DataType.INTEGER)
public spent!: number;
#Default(false)
#Column(DataType.BOOLEAN)
public isMessageReaded!: boolean;
#Default(false)
#Column(DataType.BOOLEAN)
public sendNotifications!: boolean;
//
#Column
public messageContainerDto!: string;
#Column(DataType.STRING)
public lastUsedCardNum!: string;
#HasMany(() => Card)
public cards!: Card[];
#ForeignKey(() => Role)
#Default(UserRole.User)
#Column(DataType.INTEGER)
public roleId!: number;
#BelongsTo(() => Role)
public role!: Role;
#HasMany(() => PostTip)
public tips!: PostTip[];
#HasMany(() => PostComment)
public comments!: PostComment[];
#HasMany(() => PostLike)
public likes!: PostLike[];
}
Avatar model:
import { Optional } from "sequelize";
import {
Column,
Table,
Model,
DataType,
ForeignKey,
BelongsTo,
} from "sequelize-typescript";
import User from "../user/user.model";
interface AwsAvatarAttributes {
id: number;
src: string;
}
interface AwsAvatarCreationAttributes
extends Optional<AwsAvatarAttributes, "id"> {}
#Table({ tableName: "aws_avatars" })
export default class AwsAvatar extends Model<
AwsAvatarAttributes,
AwsAvatarCreationAttributes
> {
#ForeignKey(() => User)
#Column(DataType.INTEGER)
public userId!: number;
#BelongsTo(() => User, { onDelete: "CASCADE" })
public user!: User;
#Column(DataType.STRING)
public src!: string;
}
I know it can be done smth like that, but cmon, I believe that there is any way to join avatar table and not for each loop every time when fetching data:
private getPostTips = (req: Request, res: Response, next: NextFunction) => {
const query = req.query as unknown as { name: string } & PaginationDto;
const { limit, offset } = getPagination(query.page, query.size);
const condition = query.name
? {
name: {
[Op.iLike]: "%" + query.name + "%",
},
}
: {};
const order: any[string] = [[query.sort, query.order]];
PostTip.findAndCountAll({
raw: true,
nest: true,
attributes: {
include: [
[db.sequelize.col(`"user"."name"`), "name"],
[db.sequelize.col(`"user"."email"`), "email"],
],
},
offset: offset,
limit: limit,
order: order,
where: { postId: req.params.id },
include: [
{
model: User,
as: "user",
attributes: [],
where: condition,
include: [{ model: AwsAvatar, as: "avatar" }],
},
],
}).then((result) => {
**const sorted: any[] = [];
for (let i = 0; i < result.rows.length; i++) {
let item = {
avatar: result.rows[i].user.avatar,
...result.rows[i],
user: undefined,
};
sorted.push(item);
}**
const data = getPagingData(sorted, result.count, query.page, limit);
return res.send(data);
});
};
This question already has answers here:
How do I cast a JSON Object to a TypeScript class?
(28 answers)
How to parse a JSON object to a TypeScript Object
(11 answers)
How do I initialize a TypeScript Object with a JSON-Object?
(18 answers)
Closed 1 year ago.
I'm using Angular to call an external API. Json data is in format like:
[
{
"AccessGroupsIdList": [],
"FirstName": "Greg",
"LastName": "Tipton",
"LocationIdList": [],
"PermissionProfile": {
"Name": "Agent",
"PermissionProfileId": {
"ID": "xy678219-bd7c-103d-b56b-1f1234a85990"
},
"Type": 3
},
"ManagerName": "Gilchrist, George",
"Status": true,
"UserGroupID": {
"ID": "00000000-0000-0000-0000-000000000000"
},
"UserGroupName": "ROOT",
"UserId": {
"ID": "4445cc66-819a-4da0-8fbf-d0bb8ce65941"
}
}
]
How do I create a class in typescript to read it since json data is nested?
export class Employees
{
AccessGroupsIdList: string[];
FirstName: string;
LastName: string;
LocationIdList : number[];
PermissionProfile ??
ManagerName: string;
Status: boolean;
UserGroupID ??
UserGroupName : string;
UserId ??
}
Please guide if the PermissionProfile, PermissionProfile will be separate nested classes?
How do I declare those?
To extend Andrew Halil's answer, I would use interfaces instead of classes in your definitions, since there do not appear to be any class methods involved; you are just describing the shape of a JSON object returned from a server
export interface Employee
{
AccessGroupsIdList: string[];
FirstName: string;
LastName: string;
LocationIdList : number[];
PermissionProfile: PermissionProfile;
ManagerName: string;
Status: boolean;
UserGroupId: ID;
UserGroupName : string;
UserId: ID;
}
export interface PermissionProfile
{
name: string;
permissionProfileId: ID;
type: string;
}
export interface ID
{
id: string;
}
Now as for an implementation, I don't use Angular all that much but you would do something like this to get the items typed
async function listEmployees(): Promise<Employee[]> {
// Make a fetch call to the API endpoint
const data = await fetch('https://some-api-endpoint.web/employees')
// if the response comes back ok, return the JSON-ified response.
.then(res => {
if(res.ok) return res.json()
return [];
});
// Instruct typescript that "data" is to be treated as an array of Employee elements.
return data as Employee[]
}
Try declaring the Typescript class structures as follows:
export class Employees
{
AccessGroupsIdList: string[];
FirstName: string;
LastName: string;
LocationIdList : number[];
PermissionProfile: PermissionProfile;
ManagerName: string;
Status: boolean;
UserGroupId: UserGroupID;
UserGroupName : string;
UserId: UserID;
}
export class PermissionProfile
{
name: string;
permissionProfileId: PermissionProfileID;
type: string;
}
export class PermissionProfileID
{
id: string;
}
export class UserGroupID
{
id: string;
}
export class UserID
{
id: string;
}
I would suggest to name the property names consistently with an Id (e.g. with UserGroupId). The name and type class property names are valid in TypeScript (unlike with the C# syntax).
I'm trying to build a Azure cognitive text translator app in angular. Initially I need to load the supported languages. But when I use this link https://api.cognitive.microsofttranslator.com/languages?api-version=3.0. I'm getting response like this
"translation": {
"af": {
"name": "Afrikaans",
"nativeName": "Afrikaans",
"dir": "ltr"
},
"ar": {
"name": "Arabic",
"nativeName": "العربية",
"dir": "rtl"
},
"bg": {
"name": "Bulgarian",
"nativeName": "Български",
"dir": "ltr"
},
...
}
How to create a model that holds this type of json?
When I tried json2ts.com for building the model, it creates object for each language like
export interface Af {
name: string;
nativeName: string;
dir: string;
}
export interface Ar {
name: string;
nativeName: string;
dir: string;
}
export interface Bg {
name: string;
nativeName: string;
dir: string;
}
export interface Translation {
af: Af;
ar: Ar;
bg: Bg;
}
Do I need to create interface for all the available languages?
are there any other simple way to handle this?
The translation property of that object is a key-value map.
You could model the whole thing as follows:
type Dir = "ltr" | "rtl";
interface Translation {
name:string;
nativeName:string;
dir: Dir;
}
interface LangResponse {
translation: {[langCode:string]: Translation};
}
You could define a Typescript interface modeling the JSON data.
export interface LanguageInfo {
name: string
nativeName: string
dir: string
code?: string
toScripts?: Array<LanguageInfo>
}
export interface TranslationLanguages {
langCode: string
info: LanguageInfo
}
function fromJSON(json_data: string | Object): TranslationLanguages {
let obj: TranslationLanguages;
if (typeof json_data === "object") {
obj = json_data as TranslationLanguages;
} else {
obj = JSON.parse(json_data);
}
return obj;
}
I've currently got a local JSON file which holds the data for numerous properties. The idea is to load this JSON file into my app, and display it into a list with options to sort it (aka rearrange the data).
Here's my property.model.ts
export class Property {
ID: number;
description: string;
price: string;
agreementType: string;
streetName: string;
houseNumber: number;
postCode: string;
place: string;
image: Image[];
status: string;
constructionYear: number;
areaSize: number;
numberOfRooms: number;
numberOfBedrooms: number;
garageType: string;
garageCapacity: number;
}
export class Image {
ID: number;
base64: string;
}
This is what my json file looks like:
[
{
"ID": 1,
"description": "Lorem ipsum...",
"price": "€800,25",
"agreementType": "unknown",
"streetName": "street",
"houseNumber": 55,
"postCode": "postCode",
"place": "place",
"image": [
{
"ID": 1,
"base64": ""
},
{
"ID": 2,
"base64": ""
},
{
"ID": 3,
"base64": ""
}
],
"status": "status",
"constructionYear": 1999,
"areaSize": 50,
"numberOfRooms": 5,
"numberOfBedrooms": 2,
"garageType": "",
"garageCapacity": 0
},
{
//...
}
]
and here is my property.service.ts
export class PropertyService {
public propertyData: Observable<Property>;
constructor(private http: HttpClient) {
this.propertyData = this.observableJson();
}
observableJson() : Observable<Property> {
return this.http.get('/data/property.json')
.map((res:Response) => { res.json(); console.log(res.json())})
.catch((error:any) => Observable.throw(error.json().error || 'Server error'));
}
}
Later on, I want to be able to use my service elsewhere within the app as well, to (for instance) add a property object to it or something. Though I don't know if this is possible like that. But for now I just want to be able to somehow have my obserable array be useable in the property component. Which, at the moment, doesn't seem to work because when I use console.log(JSON.stringify(this.propertyData)); inside the constructor, I get the following error:
JS: ERROR Error: Uncaught (in promise): TypeError: Converting circular structure to JSON
JS: TypeError: Converting circular structure to JSON
now, a google search tells me that this is because it's a nested JSON object, but after many attempts I haven't been able to work out how to solve this.
Thanks in advance for any help.
The property Observable probably has something self-referential inside of it which results in the error you're seeing. You don't want to JSON.stringify propertyData which is an Observable, but you want to stringify the emitted JSON response. There are a lot of different ways to do this and it depends on the circumstances of where you are using it. For example:
this.propertyData.subscribe(data => JSON.stringify(data));
const data = JSON.stringify(await this.propertyData.toPromise());
Try this -
this.observableJson().subscribe(res => {
this.propertyData = res;
console.log(JSON.stringify(this.propertyData));
});
I'm going crazy.
I have this JSON:
{
'name': 'Help Me',
'filters': {
'filter1': {
'filter_id': 'wow',
'filter_query': 'maw',
},
'filter2': {
'filter_id': 'wow',
'filter_query': 'maw',
}
}
}
And i'm trying to get this in this way:
export interface MyObject {
name: string;
filters: Filters;
}
export interface Filters {
[key: string]: QueryFilter;
}
export interface QueryFilter {
filter_id: string;
filter_query: string;
friendly_filter_query: string;
}
Or in this way:
export interface MyObject {
name: string;
filters: Map<string, QueryFilter[]>;}
But in first case i got this error message:
Property 'filters' is missing in type '{ 'name': string; ...'.
And in the second case i got this:
Property 'clear' is missing in type '{ 'filter1': { 'filter_id': string; 'filter_query': string; }...'.
I really can't figure out.