是否有本地功能将基于字符串的 JSON 转换为 Mongoose 模式对象实例?

11

我正在使用Express,想要寻找一种方便的方法来转换这种对象(它是从请求 req.body.myObject 中获取的):

{
  "name": "Foo",
  "someNumber": "23",
  "someBoolean": "on"
}

将其转换为此模式的实例:

var myObjectSchema = new Schema({
    name: String,
    someNumber: Number,
    someBoolean: Boolean
});

注意第一个对象来自请求,因此它完全由字符串构成。

是否有什么好的方法来实现这一点?如果没有,您有关于如何将此特性实现为中间件的任何建议吗?


1
请注意,第一个对象来自请求,因此它完全由字符串构成。为什么不在适当的位置返回数字和布尔值?你在引用的句子中使用的“so”暗示只允许字符串,但如果是JSON,则不是这样。也就是说,JSON本身是一种基于字符串的格式,但它可以表示数字和布尔值,当解析JSON时,这些属性变成数字和布尔值... - nnnnnn
你的架构中的属性是固定的还是需要动态的? - Mahn
@nnnnnn 嗯,问题在于Express从表单myObject[name]:name, myObject[someNumber]:23中获取信息,并且默认情况下将每个属性设置为字符串来构建myObject! - Renato Gama
3个回答

11

参考这个线程Mongoose:将JS对象直接插入数据库,我发现确实有一个内置功能可以做到这一点。

你只需构建一个新模型,将请求值(来自表单)作为参数传递:

function add(req, res){
    new Contact(req.body.contact).save(function(err){
        console.log("Item added");
        res.send();
    });
};

它会自动为你转换一些东西!


1
function add(req, res){ Contact(req.body.contact).save(function(err){ console.log("已添加项目"); res.send(); }); };请注意,您不需要在Contact前面加上“new”。这在Node中是昂贵的。 - fino
嗨@fino,谢谢你的建议!你能提供一些关于“new”在Node中成本高昂的资源吗?作为一个初学者,我还没有听说过这个!谢谢。 - Renato Gama
很多原因,Eric在这里提出了一些好的理由http://ericleads.com/2012/09/stop-using-constructor-functions-in-javascript/。对于Node.js,搜索Node.js内存管理和堆大小即可。 - fino
谢谢@fino,我一定会看看的! - Renato Gama

2

我知道这个答案已经被接受了,但是我想指出mongoose会为您处理大部分的类型转换...大多数情况下。尽管mongoose实现了这个便利功能,但它也把mongo真正的行为隐藏起来了。例如,mongoose让你可以像这样做:

PersonModel.findById("4cdf00000000000000007822", ...);

然而,如果你尝试直接查询数据库(不使用mongoose),这样是无法实现的:

PersonCollection.find({_id: "4cdf00000000000000007822"}, ...);

这是因为ObjectIds不是字符串,而是对象。在内部,mongoose将该字符串转换为ObjectId,然后对数据库执行查询,以便最终的查询看起来像这样:

PersonCollection.find({_id: ObjectId("4cdf00000000000000007822")}, ...);

此外,模式中的每个路径都有一个“转换器”方法。这是一个私有方法,但当您需要它时,它非常方便。请注意,下面描述的caster方法未经记录,可能会在没有警告的情况下更改。使用时请自行承担风险(抱歉,我不是故意大声说话)。
// Use PersonModel.schema.paths() to get all paths and loop over them if you want
var key = "name";
var pathObj = PersonModel.schema.path( key );
if( !pathObj ) pathObj = PersonModel.schema.virtualpath( key );
if( !pathObj ) { /* not found: return, continue, exit, whatever */ }

// UNDOCUMENTED: USE AT YOUR OWN RISK
var caster = pathObj.caster || pathObj;
var castedValue = caster.cast( req.body.name );

我知道这是为什么?因为如果你想使用MongoDB的一些高级功能,例如聚合,你需要在构建管道时自己转换数据类型。我也需要手动转换某些查询的值,这些查询使用了$in操作符...也许现在不再需要这样做了。重点是,如果你无法获得预期的结果,请尝试自己转换数值。


在研究后,如果您在生产中使用这个,那您一定是疯了。哎呀! - Joshua Michael Calafell
1
不使用单元测试... 人们经常使用未记录的功能,如果你测试你的代码,那么你是100%安全的。如果方法消失了,你的测试会在你进入生产之前很早就发现它,并进行修复。我也警告说需要自行承担使用的风险。如果你不编写测试,那就是你的问题,这是你要承担的风险 ;) - Ryan Wheale

0

如果模式是静态的,理论上可以采用懒惰、不太复杂的方式,直接硬编码值而不是传递对象本身:

var someObject = {
    name: "Foo",
    someNumber: "23",
    someBoolean: "on"
}

var myObjectSchema = new Schema({
    name: someObject.name,
    someNumber: parseInt(someObject.someNumber, 10),
    someBoolean: (someObject.someBoolean == "on")
});

也许这不是你想要的答案,但如果没有更好的选择,这可能是值得考虑的。


1
正如你所想,我正在寻找一种通用的方法,在每次编写新模式时无需重写它。 - Renato Gama

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接