class-transformer
class-transformer copied to clipboard
serialize @Exclude not working with inheritance
similar to #59 , in serialization, @Exclude on base class doesn't work too.
@Exclude()
export abstract class AbstractEntity {
@PrimaryGeneratedColumn()
id: number;
@CreateDateColumn()
createdAt: Date;
@UpdateDateColumn()
updatedAt: Date;
}
@Entity()
export class User extends AbstractEntity {
@Column({
length: 32,
unique: true
})
username: string;
@Exclude()
@Column({
nullable: true
})
password: string;
@Column({
nullable: true
})
phone: string;
@Column("json")
roles: Role[];
@Column()
lastLoginAt: Date;
}
@TransformClassToPlain()
@Get("/test")
test2() {
let u = new User();
u.id = 1;
u.createdAt = new Date();
u.username = "test";
u.password = "no";
u.roles = ["USER"] as any;
return u;
}
response body:
{
"id": 1,
"createdAt": "2017-09-29T08:43:47.697Z",
"username": "test",
"roles": ["USER"]
}
there shouldn't have id and createdAt.
Does putting @Exclude on the properties of the abstract class (instead of the class) helps?
also the same, actually there is no real abstract in js
Yeah, I just ran into this too.
The @exclude will enforce for plainToClass, but not the other way around.
In fact, none of the decorators get used when using classToPlain
Just found this today, reported this problem a month ago at https://github.com/typestack/class-transformer/issues/1097 .
class-level expose/exclude will not be inherited because class-level metadata storage doesn't use the inheritance pattern used for property-level metadata.
So at the moment one needs to use one of these workarounds: a) decorate ALL child classes with the same class-level decorator b) configurate class-transformer to use exposeAll/excludeAll strategy explicitly (can lead to other issues especially when using nested classes)
I'm interested to implement this behavior to improve quality of my own projects depending on this to work. @NoNameProvided would you take a PR for this into account?
@jbjhjm could you explain the workarounds in more details please ?
@Mageek627 none of the workarounds are perfect, but they may work depending on your situation. Following examples are based on the example Models of the first post here (AbstractEntity , User).
Workaround a) Add the @Exclude decorator not just to the base class AbstractEntity but to all children too. With User being decorated with @Exclude, class-transformer will find the metadata and work as expected. Not a great solution because it is easy to forget and hard to notice that it is missing.
Workaround b)
Enforce behavior independent of the passed Entity/Model
classToPlain(user, { strategy: 'excludeAll' }) would MOSTLY work the same as transforming a @Exclude() User {...}.
However there are two weak points:
- As it is imparative behavior it will be applied to all Models, not taking into account if they are decorated with expose/exclude
- The behavior will be forced deeply, so if you transform a Model that has submodels, you need to keep in mind these must use the same setup too. The workaround can work nicely if you ALWAYS mark all props with @Expose and NEVER use plain/interface based subobjects.
Workaround b2) The concept of workaround b could be extended: before passing data to class transformer, write custom logic that checks if the passed class or any of its parents has Expose/Exclude metadata set. If so, set strategy option to excludeAll/exposeAll. This would compensate missing functionality as much as possible at the moment. It would still be deep behavior so there may be issues if your model has nested objects.