Class-transformer: classToPlain fails to transform mongo ObjectId to string

Created on 15 Sep 2017  路  6Comments  路  Source: typestack/class-transformer

Hello guys,
I created an endpoint with routing-controller, from @pleerock :

import { JsonController, Param, Get, NotFoundError } from "routing-controllers";

@JsonController("my-route")
export class MyController {
    @Get("/:marketingId")
    async getOne(@Param("marketingId") marketingId: string) {
        let marketing: MarketingAutomation = await MyDB().findOne({
            '_id': new ObjectId(marketingId)
        });
        console.log(marketing._id instanceOf ObjectId) // writes true

        if (!!marketing && marketing._id) {
            return classToPlain(marketing);
        }

        throw new NotFoundError(`User was not found.`);
    }
}

The model, MarketingAutomation is:

import { ObjectId } from "mongodb";
import { IsDefined, IsString } from 'class-validator';
import { Expose } from 'class-transformer';

export class MarketingAutomation {
    _id?: ObjectId;
    companyId?: ObjectId;

    @IsDefined()
    @IsString()
    name: string;

    @Expose({name: '_id', toPlainOnly: true})
    id() { return this._id.toHexString();}

    @Expose({name: 'companyId', toPlainOnly: true})
    _companyId() { return this.companyId.toHexString();}    
}

The response is :

{
    "_id": {
        "_bsontype": "ObjectID",
        "id": {"0":89,"1":186,"2":173,"3":122,"4":253,"5":210,"6":194,"7":101,"8":17,"9":9,"10":73,"11":26}
    },
    "name": "test",
    "companyId": {
        "_bsontype": "ObjectID",
        "id": {"0":87,"1":63,"2":67,"3":251,"4":214,"5":195,"6":157,"7":16,"8":0,"9":72,"10":9,"11":211}
    }
}

The response I would like to receive is:

{
    "_id": "59baad7afdd2c2651109491a",
    "name": "test",
    "companyId": "rf41237afdd2c2651109gt45"
}

When you look at the response, you see that _id and companyId are not transformed in strings
Do you know how to achieve it ?

Thank you very much !

question

Most helpful comment

I have the same issue. Looks like there is some implicit to string conversion. I found the following workaround:

    @Expose()
    public get id() {
        return this._id ? this._id.toHexString() : undefined;
    }

    @Exclude()
    // tslint:disable-next-line:variable-name
    public _id?: ObjectId;

All 6 comments

Can't reproduce this behavior, but I think you have name conflict. Try to add @Exclude to ObjectId properties:

  @Exclude({toPlainOnly: true})
  _id?: ObjectID;
  @Exclude({toPlainOnly: true})
  companyId?: ObjectID;

Do you really need these id and _companyId getters?
You can add @Transform decorator to existing properties instead:

@Transform((id: ObjectID) => id.toHexString(), {toPlainOnly: true})
_id?: ObjectID;
@Transform((id: ObjectID) => id.toHexString(), {toPlainOnly: true})
companyId?: ObjectID;

Thank @Petr0vi4, I tried it but it fails:

    @Type(() => ObjectId)
    @Transform(value => value.toHexString(), {toPlainOnly: true})
    _id?: ObjectId;

```json
{
"name": "TypeError",
"message": "Cannot read property 'prototype' of undefined",
"stack": "TypeError: Cannot read property 'prototype' of undefined.../node_modules/class-transformer/metadata/MetadataStorage.js:131:84"
}

Do you know what happens ?
I found an alternative but it is not as clean:

```ts
let instance = plainToClass(classType, JSON.parse(JSON.stringify(plain)));
return classToPlain(instance);

I have the same issue. Looks like there is some implicit to string conversion. I found the following workaround:

    @Expose()
    public get id() {
        return this._id ? this._id.toHexString() : undefined;
    }

    @Exclude()
    // tslint:disable-next-line:variable-name
    public _id?: ObjectId;

@VivienAdnot The reason for this is that when the JS engine transforms the object it calls the toJSON function recursively under the hood and ObjectId doesn't provide a custom implementation for it, so the default built-in will run what outputs the underlying object.

The proper solution is what @Petr0vi4 wrote in his second comment.

I will go ahead and close this, feel free to continue the discussion if you still have any question.

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

lazarljubenovic picture lazarljubenovic  路  6Comments

ERPedersen picture ERPedersen  路  4Comments

gregsolo-intent picture gregsolo-intent  路  5Comments

NathanHazout picture NathanHazout  路  5Comments

NoNameProvided picture NoNameProvided  路  5Comments