nodejs中使用mongoose和mongoose-long模块操作NumberLong时遇到的json格式化问题

nodejs中使用mongoose和mongoose-long模块操作NumberLong时遇到的json格式化问题

背景

项目中使用了nodejs+mongo,使用的是mongoose模块进行数据库操作,mongoose本身的SchemaTypes中不包含Long(对应mongo中的NumberLong),所以需要额外一个模块mongoose-long来让它支持。

let mongoose = require('mongoose');
require('mongoose-long')(mongoose);
var SchemaTypes = mongoose.Schema.Types;
var MyNumber = mongoose.model('my_number', { num: SchemaTypes.Long });

使用以上定义的MyNumber,使用JSON.stringify()的时候,里面的num会按照字符串输出。

var record = new MyNumber({ num: 1234556 });
console.log(JSON.stringify(record));
//输出:{"num":"123456"}

解决之道

傻办法

var record = new MyNumber({ num: 1234556 });
/*一些其他操作*/

//把类型转换成Number
record.num = record.num.toNumber();
console.log(JSON.stringify(record));
//输出:{"num":123456}

这是一种方法,但是这种方法治标不治本,在所有涉及到的地方都要改对应的值,少改一个就还是有问题。

新办法

在找办法的过程中,首先发现,JSON.stringify(obj)会首先去找有没有obj.prototype.toJSON这个方法,如果有的话就用这个方法的返回值。
所以,方向就变成去找到num的原型。一开始我以为它的类型是SchemaTypes.Long,增加SchemaTypes.Long.prototype.toJSON=function(){return this.toNumber();},测试后发现不起作用。
再经过调试发现,mongoose里面用到了一个模块bson,用到了bson中的大部分数据类型,其中就包括了bson.Long,而上面的record结构确实就是bson.Long的结构,于是,就简单了。

// 修复mongoose-long转换成json字符串时变成string而不是number的bug
require("bson").Long.prototype.toJSON=function(){return this.toNumber();}

var record = new MyNumber({ num: 1234556 });
console.log(JSON.stringify(record));
//输出:{"num":123456}

至此,问题总算解决。

扩展

在解决问题的过程中,发现mongoose的api也提供了重新实现toJSON的方式。
https://mongoosejs.com/docs/api.html#document_Document-toJSON
这个方式貌似是对model中某个具体字段进行设置的,没有细研究,有兴趣的可以研究研究。

发表评论

电子邮件地址不会被公开。 必填项已用*标注