So I have a json array like this:
[
{
"title": "This is the first title",
"image": "img/first_img.png"
},
{
"title": "This one is the second",
"image": "img/second_img.png"
}
]
My goal is to map the simple json array to typescript object array, which is defined as follows.
export class GenericField {
public Required: boolean;
public Disabled: boolean;
constructor() {
this.Required = false;
this.Disabled = false;
}
}
export class StringField extends GenericField {
private readonly _minLength: number | null;
private readonly _maxLength: number | null;
private readonly _value: string;
constructor(
minLength: number | null,
maxLength: number | null,
value: string
) {
super();
this._minLength = minLength;
this._maxLength = maxLength;
this._value = value;
}
// ...
}
export class UrlField extends GenericField {
private _url: string;
constructor(url: string) {
super();
this._url = url;
}
public get Url(): string {
return this._url;
}
}
export class SliderObject {
public title: StringField;
public image: UrlField;
constructor(title: string, image: string) {
this.title = new StringField(null, null, title);
this.image = new UrlField(image);
this.image.Disabled = true;
}
}
When I map with class-transformer
, the objects aren't mapped correctly. How do I achieve that I can access Required
field and Disabled
field, which is not present in json.
Stackblitz
See my StackBlitz example here
CodePudding user response:
It would be like the following (assuming response.data
is where the JSON text is).
const items: Array<{title: string, image: string}> = JSON.parse(response.data)
const sliderObjects = items.map(item =>
new SliderObject(item['title'], item['image']))
CodePudding user response:
You can come up with your own reviver and conventions in your models to support proper deserialization.
class SliderObject {
public title: StringField;
public image: UrlField;
constructor(title: string, image: string) { /* class initialization */ }
static fromJSON(data: { image: string; title: string; }): SliderObject {
return new SliderObject(data.title, data.image);
}
static isDeserializableFromJSON(data: any): data is { image: string; title: string; } {
return data && typeof(data.image) === 'string' && typeof(data.title) === 'string';
}
}
function jsonReviver(_: string, value: unknown): unknown {
if (SliderObject.isDeserializableFromJSON(value)) {
return SliderObject.fromJSON(value);
}
return value;
}
console.log(JSON.parse(`[/* array data */]`, jsonReviver));
In the example above I have added to static methods to SliderObject to help with serialization and a custom json reviver function. The static methods detect if data create from JSON.parse can be used to create a sliderObject and the other actually creates the new class. The reviver will call the guard method and return a SliderObject if the guard passed.
There are lots ways to skin this cat. I personally eschew TypeScript classes in favor of interfaces which I find are more flexible. Why try to make TypeScript C# or Java? By using interfaces, I can largely avoid situations like these.
CodePudding user response:
Please check some updates that I made in order to have your object attributes accessible:
https://stackblitz.com/edit/new-project-w5b97l?file=src/app/field-definition.ts
A few notes:
The
GET
request from theslider_object.json
file is NOT returning anObservable<SliderObject[]>
but anObservable<SliderObjectDTO[]>
(or some other name for the interface). An instance ofSliderObject
has theStringField
attribute and theUrlField
attribute, while what you are reading from the file are JSON objects (NOTSliderObject
instances) which check theSliderObjectDTO
interface, so they will have thetitle
andimage
properties.observables should follow this naming convention:
observableName$
, so with a$
symbol at the end, thussliderObjects
renamed tosliderObjects$
class attributes should not start with a capital letter, thus
Required
renamed torequired
,Disabled
renamed todisabled
. The same thing applies for the properties (getters)