Imperfect, yet suitable for most scenarios: substitute the forbidden characters with something else. As they are part of keys, these new characters should be uncommon.
/** To ensure compatibility for MongoDB insert, this function replaces \ with ⍀, ^$ with '₴', and dots with ⋅ in the object.
Caveats:
1. If your original documents contain ⍀, ₴ or ⋅, they will be converted to \$ upon decoding.
2. Recursive structures pose challenges. Limiting levels can prevent a stack overflow. The default max level is set at 10.
*/
encodeMongoObj = function(o, level = 10) {
var build = {}, key, newKey, value
//if (typeof level === "undefined") level = 20 // default level if not provided
for (key in o) {
value = o[key]
if (typeof value === "object") value = (level > 0) ? encodeMongoObj(value, level - 1) : null // Recurse if object
newKey = key.replace(/\\/g, '⍀').replace(/^\$/, '₴').replace(/\./g, '⋅') // Replace prohibited chars in mongo keys
build[newKey] = value
}
return build
}
/** Decode an object encoded using the above function. Assumes non-recursive structure from Mongodb */
decodeMongoObj = function(o) {
var build = {}, key, newKey, value
for (key in o) {
value = o[key]
if (typeof value === "object") value = decodeMongoObj(value) // Recurse if object
newKey = key.replace(/⍀/g, '\\').replace(/^₴/, '$').replace(/⋅/g, '.') // Replace prohibited chars in mongo keys
build[newKey] = value
}
return build
}
Try it out:
var nastyObj = {
"sub.obj" : {"$dollar\\backslash": "$\\.end$"}
}
nastyObj["$you.must.be.kidding"] = nastyObj // Make it recursive
var encoded = encodeMongoObj(nastyObj, 1)
console.log(encoded)
console.log( decodeMongoObj( encoded) )
Here are the results - note that the values remain unaltered:
{
sub⋅obj: {
₴dollar⍀backslash: "$\\.end$"
},
₴you⋅must⋅be⋅kidding: {
sub⋅obj: null,
₴you⋅must⋅be⋅kidding: null
}
}
[12:02:47.691] {
"sub.obj": {
$dollar\\backslash: "$\\.end$"
},
"$you.must.be.kidding": {
"sub.obj": {},
"$you.must.be.kidding": {}
}
}