class-transformer icon indicating copy to clipboard operation
class-transformer copied to clipboard

serialize @Exclude not working with inheritance

Open Diluka opened this issue 8 years ago • 6 comments

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.

Diluka avatar Sep 29 '17 09:09 Diluka

Does putting @Exclude on the properties of the abstract class (instead of the class) helps?

NoNameProvided avatar Sep 30 '17 12:09 NoNameProvided

also the same, actually there is no real abstract in js

Diluka avatar Oct 01 '17 06:10 Diluka

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

emlynmac avatar Oct 31 '17 21:10 emlynmac

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 avatar Mar 01 '22 11:03 jbjhjm

@jbjhjm could you explain the workarounds in more details please ?

Mageek627 avatar Mar 02 '22 10:03 Mageek627

@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:

  1. As it is imparative behavior it will be applied to all Models, not taking into account if they are decorated with expose/exclude
  2. 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.

jbjhjm avatar Mar 02 '22 11:03 jbjhjm