NestJS中class-transformer多子类型序列化异常问题
NestJS class-transformer鉴别器序列化异常:所有枚举类型被转为第一个子类型
问题场景
我用NestJS配合class-validator/transformer开发,有一个包含两种类型的数组字段。原本通过@Type装饰器加鉴别器定义数组项类型,同时用@Transform避免序列化出空对象,但现在遇到了问题:
鉴别器是个有7个值的枚举,其中6个对应ClassONE,1个对应ClassTWO,但序列化时不管原枚举类型是什么,都会被转换成配置里第一个子类型的枚举值。
代码示例
enum DiscrType { A = "a", B = "b", C = "c" } class Discriminator { @IsEnum(DiscrType) type: DiscrType; } class ClassONE extends Discriminator { // 自定义属性 } class ClassTWO extends Discriminator { // 自定义属性 } class Stuff { @Type(() => Discriminator, { discriminator: { property: 'type', subTypes: [ { value: ClassTWO, name: DiscrType.C}, { value: ClassONE, name: DiscrType.A}, // 原类型是B也会被转为A { value: ClassONE, name: DiscrType.B}, ], }, keepDiscriminatorProperty: true, }) @Transform( ({ value }) => value?.map((object: ClassONE | ClassTWO ) => object.type === DiscrType.C ? plainToClass(ClassTWO, object) : plainToClass(ClassONE, object), ), ) @ValidateNested({each: true}) property: (ClassONE | ClassTWO )[]; }
表现
创建实例new Stuff({type: DiscrType.B, ...})后用Class Serializer序列化:
- 预期结果:
{type: "b", ...} - 实际结果:
{type: "a", ...}
问题原因
class-transformer的鉴别器逻辑中,当多个子类型配置对应同一个类(比如DiscrType.A和DiscrType.B都指向ClassONE),序列化时会自动匹配第一个符合类的子类型配置,把所有ClassONE实例的type字段替换成第一个配置的DiscrType.A,覆盖了原有的枚举值。
解决方案
方案一:保留鉴别器,修复字段覆盖问题
移除重复的ClassONE子类型配置,同时在@Transform中强制保留原type值:
class Stuff { @Transform(({ value }) => { if (!value) return []; return value.map((object) => { const targetClass = object.type === DiscrType.C ? ClassTWO : ClassONE; const instance = plainToClass(targetClass, object); // 强制保留原始type值,避免被鉴别器覆盖 instance.type = object.type; return instance; }); }) @ValidateNested({ each: true }) @Type(() => Discriminator, { discriminator: { property: 'type', subTypes: [ { value: ClassTWO, name: DiscrType.C }, { value: ClassONE, name: DiscrType.A }, // 只保留一个ClassONE映射 ], }, keepDiscriminatorProperty: true, }) property: (ClassONE | ClassTWO)[]; }
方案二:简化配置,用动态类型处理
直接去掉鉴别器的多子类型配置,通过@Type的动态函数处理反序列化类型,@Transform处理序列化逻辑:
class Stuff { @Transform(({ value }) => { if (!value) return []; return value.map((object) => { const targetClass = object.type === DiscrType.C ? ClassTWO : ClassONE; return plainToClass(targetClass, object); }); }) @ValidateNested({ each: true }) @Type(({ object }) => { // 反序列化时根据当前对象的type动态返回对应类 return object.type === DiscrType.C ? ClassTWO : ClassONE; }) property: (ClassONE | ClassTWO)[]; }
这个方案更简洁,完全避开了鉴别器的默认行为冲突,同时保证反序列化和序列化都能正确处理类型和字段。
内容的提问来源于stack exchange,提问作者Branchverse
相关产品推荐
相关产品推荐

