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

fix: object id is transformed into a different one when use plainToClass

Open x-shadow-x opened this issue 2 years ago • 10 comments

Description

object id is transformed into a completely different one when we add excludeExtraneousValues: true in plainToClass function

Minimal code-snippet showcasing the problem

import { Expose, plainToClass, Transform } from "class-transformer"
import { IsMongoId } from "class-validator"
import { ObjectId } from "mongodb"
import mongoose from "mongoose"

export class SampleWithObj {
  @Transform(({ obj }) => String(obj.id), { toClassOnly: true })
  @Expose()
  @IsMongoId()
  id: string

  static fromObject(obj: unknown): SampleWithObj {
    if (!obj) return null
    return plainToClass(SampleWithObj, obj)
  }
}

export class SampleWithValue {
  @Transform(({ value }) => String(value), { toClassOnly: true })
  @Expose()
  @IsMongoId()
  id: string

  static fromObject(obj: unknown): SampleWithValue {
    if (!obj) return null
    return plainToClass(SampleWithValue, obj)
  }
}

const printCls = (obj: { id: ObjectId }) => {
  console.log("plain obj: ", obj)
  console.log("deserialized obj: ", { id: String(obj.id) })

  console.log("============= test result ============")

  console.log("obj with String(): ", SampleWithObj.fromObject({ ...obj }))

  console.log("value with toString(): ", SampleWithValue.fromObject({ ...obj }))
}

const obj = { id: new mongoose.Types.ObjectId("61980e80fc741dcac3c60a83") }

printCls(obj)

pls also check this repo to run the code and check the result. https://github.com/x-shadow-x/class-transform-plainToClass-issue

Expected behavior

should get the correct string of objectId.

Actual behavior

image

x-shadow-x avatar Nov 20 '21 05:11 x-shadow-x

I am encountering the same issue when using plainToClass method, all objectIds are changed. Any solution to the issue?

this happened after upgrading to mongoose 6.0.11.

alexreal1314 avatar Nov 29 '21 15:11 alexreal1314

This might be a similar issue https://github.com/typestack/class-transformer/issues/879

feelgood-interface avatar Nov 30 '21 11:11 feelgood-interface

image Still the same

pacificescape avatar Jan 10 '22 18:01 pacificescape

Any workaround? I've the same issue when using class-transformer within NestJs. I'm using mongoose 6.1.7 and class-transformer 0.5.1

Not sure if it can helps other foxes, but I'm currrently using

  @Transform((value) => value.obj._id.toString())
  _id: ObjectId; 

as workaround

jaytonic avatar Feb 02 '22 19:02 jaytonic

@jaytonic It's good working for me. Thanks!

huypn avatar Sep 10 '22 14:09 huypn

Just encountered this as well

chenshuiluke avatar Jan 06 '23 16:01 chenshuiluke

+1 it returns new ObjectId instead of provided one

dima-mamaev avatar Jan 18 '23 13:01 dima-mamaev

Previously we can omit @Type(() => String) But I realised that it case this issue, we need to add @Type(() => String) when ever we use @Transform with plainToClass to transform ObjectId to String

tonynguyenit18 avatar Feb 14 '23 10:02 tonynguyenit18

@jaytonic and @tonynguyenit18 are right, @Type(() => String) works a charm. In my case this is a return type so transforming into a string is actually probably better than passing around a ObjectId because in any request it's going to need to come in as a string anyway.

It would be interesting to get insights from someone more versed in the workings of class transformer but something tells me this isn't an issue with the library but rather with our usage. If we put an objectId in, my guess is class transformer just creates a new one from the whole object (not just the string id) and mongoDB may not have a good way of dealing with this? @Type(() => String) works though because ObjectId will have overridden the toString() function to just return the id. I'm making a lot of assumptions here though.

If you really did want to return the id, you could do this:

  @Transform((value) => new Types.ObjectId(value.obj._id.toString()))
  _id: Types.ObjectId;

I have tested it and it will create an objectId with the same Id, but think twice wether you really want to be doing this, it seems a bit pointless to me as the only advantage that you might have from using an ObjectId instead of the string is that you store generation time, but that's going to be reset every time you run this anyway.

lclarkg18 avatar Mar 11 '23 14:03 lclarkg18