Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom toJSON and toObject Implementation Produces Object Instead of Plain Value #15084

Open
1 task done
men232 opened this issue Dec 9, 2024 · 2 comments
Open
1 task done
Labels
new feature This change adds new functionality, like a new method or class
Milestone

Comments

@men232
Copy link

men232 commented Dec 9, 2024

Prerequisites

  • I have written a descriptive issue title

Mongoose version

7.6.3

Node.js version

22.10

MongoDB version

5

Operating system

macOS

Operating system version (i.e. 20.04, 11.3, 10)

No response

Issue

I'm encountering an issue with implementing custom toJSON and toObject behavior for a custom type. My goal is to produce different outputs when converting the custom type's value—specifically, I want to output a plain number or string instead of the object representation.

However, despite my efforts, the result always includes the object representation, such as: value: CustomType { value: 1 },.

Below is a minimal reproduction of the issue:

class SchemaCustomType extends mongoose.SchemaType {
  static schemaName = 'CustomType';

  constructor(key, options) {
    super(key, options, 'CustomType');
  }

  cast(value) {
    if (value === null) return null;
    return new CustomType(value);
  }
}

class CustomType {
  constructor(value) {
    this.value = value;
  }

  toObject() {
    return this.value;
  }

  $toObject() {
    return this.value;
  }

  toJSON() {
    return String(this.value);
  }
}

mongoose.Schema.Types.CustomType = SchemaCustomType;

const Model = mongoose.model(
  'Test',
  new mongoose.Schema({
    value: { type: mongoose.Schema.Types.CustomType },
  }),
);

const doc = new Model();
doc.value = 1;

console.log(doc.toJSON()); // expected { value: '1' }
console.log(doc.toObject()); // expected { value: 1 }

I would greatly appreciate any guidance on resolving this issue or understanding why the current implementation doesn't work as expected.

@men232 men232 added help This issue can likely be resolved in GitHub issues. No bug fixes, features, or docs necessary help wanted labels Dec 9, 2024
@Ayanabilothman
Copy link

The issue here is that you need to modify the toJSON and toObject of the document itself, not just the value field. If you try to print doc.value.toJSON() it will work as expected and will return "1".
So you can pass more options to the schema to modify toJSON and toObject. Here is the code:

const Model = mongoose.model(
  "Test",
  new mongoose.Schema(
    {
      value: { type: mongoose.Schema.Types.CustomType },
    },
    {
      toJSON: {
        transform(doc, ret) {
          // Customize the output for toJSON
          if (ret.value && typeof ret.value.toJSON === "function") {
            ret.value = ret.value.toJSON(); // Ensure value is transformed to a string
          }
          return ret;
        },
      },
      toObject: {
        transform(doc, ret) {
          // Customize the output for toObject
          if (ret.value && typeof ret.value.toObject === "function") {
            ret.value = ret.value.toObject(); // Ensure value is transformed to its raw form
          }
          return ret;
        },
      },
    }
  )
);

@vkarpov15
Copy link
Collaborator

This is something that we will need to add a new feature for. Mongoose does not currently support customizing toObject() and toJSON() output on a per SchemaType basis, but we should be able to support the following:

mongoose.Schema.Types.CustomType = SchemaCustomType;
SchemaCustomType.set('toObject', v => v?.value ?? v);

// Or:
const Model = mongoose.model(
  'Test',
  new mongoose.Schema({
    value: { type: mongoose.Schema.Types.CustomType, toObject(v) { return v?.value ?? v; }  },
  }),
);

That would also be helpful for other data types, like setting custom date formats or converting Decimal128 to number in JSON serialization.

@vkarpov15 vkarpov15 added this to the 8.10 milestone Dec 15, 2024
@vkarpov15 vkarpov15 added new feature This change adds new functionality, like a new method or class and removed help This issue can likely be resolved in GitHub issues. No bug fixes, features, or docs necessary help wanted labels Dec 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
new feature This change adds new functionality, like a new method or class
Projects
None yet
Development

No branches or pull requests

3 participants