Skip to content

Commit 35ae72f

Browse files
durranimlucas
authored andcommitted
NODE-2253: Support for v1 Extended JSON (#339)
* NODE-2253: Serialize v1 Extended JSON: - Adds a legacy: true option to serialize and stringify - Adds tests for cases where v1 and v2 extjson differ. * NODE-2253: Deserialisation support sans dbref/regex * NODE-2253: Support Regex and DBRef as legacy * Remove console log * Add legacy option in the README * add options default assignment * add options defasult guard on fromExtendedJSON too * chore(extjson): add legacy top-level default option assignment and doc strings Co-authored-by: Lucas Hrabovsky <hrabovsky.lucas@gmail.com>
1 parent cea1867 commit 35ae72f

File tree

9 files changed

+255
-30
lines changed

9 files changed

+255
-30
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ console.log(EJSON.parse(text));
215215
| [space] | <code>string</code> \| <code>number</code> | | A String or Number object that's used to insert white space into the output JSON string for readability purposes. |
216216
| [options] | <code>object</code> | | Optional settings |
217217
| [options.relaxed] | <code>boolean</code> | <code>true</code> | Enabled Extended JSON's `relaxed` mode |
218+
| [options.legacy] | <code>boolean</code> | <code>true</code> | Output in Extended JSON v1 |
218219

219220
Converts a BSON document to an Extended JSON string, optionally replacing values if a replacer
220221
function is specified or optionally including only the specified properties if a replacer array

lib/binary.js

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -280,12 +280,19 @@ class Binary {
280280
/**
281281
* @ignore
282282
*/
283-
toExtendedJSON() {
283+
toExtendedJSON(options) {
284+
options = options || {};
284285
const base64String = Buffer.isBuffer(this.buffer)
285286
? this.buffer.toString('base64')
286287
: Buffer.from(this.buffer).toString('base64');
287288

288289
const subType = Number(this.sub_type).toString(16);
290+
if (options.legacy) {
291+
return {
292+
$binary: base64String,
293+
$type: subType.length === 1 ? '0' + subType : subType
294+
};
295+
}
289296
return {
290297
$binary: {
291298
base64: base64String,
@@ -297,9 +304,16 @@ class Binary {
297304
/**
298305
* @ignore
299306
*/
300-
static fromExtendedJSON(doc) {
301-
const type = doc.$binary.subType ? parseInt(doc.$binary.subType, 16) : 0;
302-
const data = Buffer.from(doc.$binary.base64, 'base64');
307+
static fromExtendedJSON(doc, options) {
308+
options = options || {};
309+
let data, type;
310+
if (options.legacy) {
311+
type = doc.$type ? parseInt(doc.$type, 16) : 0;
312+
data = Buffer.from(doc.$binary, 'base64');
313+
} else {
314+
type = doc.$binary.subType ? parseInt(doc.$binary.subType, 16) : 0;
315+
data = Buffer.from(doc.$binary.base64, 'base64');
316+
}
303317
return new Binary(data, type);
304318
}
305319
}

lib/db_ref.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,17 @@ class DBRef {
4646
/**
4747
* @ignore
4848
*/
49-
toExtendedJSON() {
49+
toExtendedJSON(options) {
50+
options = options || {};
5051
let o = {
5152
$ref: this.collection,
5253
$id: this.oid
5354
};
5455

56+
if (options.legacy) {
57+
return o;
58+
}
59+
5560
if (this.db) o.$db = this.db;
5661
o = Object.assign(o, this.fields);
5762
return o;

lib/double.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ class Double {
3434
* @ignore
3535
*/
3636
toExtendedJSON(options) {
37-
if (options && options.relaxed && isFinite(this.value)) return this.value;
37+
if (options && (options.legacy || (options.relaxed && isFinite(this.value)))) {
38+
return this.value;
39+
}
3840
return { $numberDouble: this.value.toString() };
3941
}
4042

lib/extended_json.js

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,14 @@ const keysToCodecs = {
3232
$numberLong: Long,
3333
$minKey: MinKey,
3434
$maxKey: MaxKey,
35+
$regex: BSONRegExp,
3536
$regularExpression: BSONRegExp,
3637
$timestamp: Timestamp
3738
};
3839

3940
function deserializeValue(self, key, value, options) {
4041
if (typeof value === 'number') {
41-
if (options.relaxed) {
42+
if (options.relaxed || options.legacy) {
4243
return value;
4344
}
4445

@@ -69,9 +70,14 @@ function deserializeValue(self, key, value, options) {
6970
const d = value.$date;
7071
const date = new Date();
7172

72-
if (typeof d === 'string') date.setTime(Date.parse(d));
73-
else if (Long.isLong(d)) date.setTime(d.toNumber());
74-
else if (typeof d === 'number' && options.relaxed) date.setTime(d);
73+
if (options.legacy) {
74+
if (typeof d === 'number') date.setTime(d);
75+
else if (typeof d === 'string') date.setTime(Date.parse(d));
76+
} else {
77+
if (typeof d === 'string') date.setTime(Date.parse(d));
78+
else if (Long.isLong(d)) date.setTime(d.toNumber());
79+
else if (typeof d === 'number' && options.relaxed) date.setTime(d);
80+
}
7581
return date;
7682
}
7783

@@ -125,7 +131,7 @@ function deserializeValue(self, key, value, options) {
125131
* console.log(EJSON.parse(text));
126132
*/
127133
function parse(text, options) {
128-
options = Object.assign({}, { relaxed: true }, options);
134+
options = Object.assign({}, { relaxed: true, legacy: false }, options);
129135

130136
// relaxed implies not strict
131137
if (typeof options.relaxed === 'boolean') options.strict = !options.relaxed;
@@ -155,6 +161,7 @@ const BSON_INT32_MAX = 0x7fffffff,
155161
* @param {string|number} [space] A String or Number object that's used to insert white space into the output JSON string for readability purposes.
156162
* @param {object} [options] Optional settings
157163
* @param {boolean} [options.relaxed=true] Enabled Extended JSON's `relaxed` mode
164+
* @param {boolean} [options.legacy=false] Output using the Extended JSON v1 spec
158165
* @returns {string}
159166
*
160167
* @example
@@ -178,7 +185,7 @@ function stringify(value, replacer, space, options) {
178185
replacer = null;
179186
space = 0;
180187
}
181-
options = Object.assign({}, { relaxed: true }, options);
188+
options = Object.assign({}, { relaxed: true, legacy: false }, options);
182189

183190
const doc = Array.isArray(value)
184191
? serializeArray(value, options)
@@ -233,6 +240,11 @@ function serializeValue(value, options) {
233240
// is it in year range 1970-9999?
234241
inRange = dateNum > -1 && dateNum < 253402318800000;
235242

243+
if (options.legacy) {
244+
return options.relaxed && inRange
245+
? { $date: value.getTime() }
246+
: { $date: getISOString(value) };
247+
}
236248
return options.relaxed && inRange
237249
? { $date: getISOString(value) }
238250
: { $date: { $numberLong: value.getTime().toString() } };
@@ -258,7 +270,7 @@ function serializeValue(value, options) {
258270
}
259271

260272
const rx = new BSONRegExp(value.source, flags);
261-
return rx.toExtendedJSON();
273+
return rx.toExtendedJSON(options);
262274
}
263275

264276
if (value != null && typeof value === 'object') return serializeDocument(value, options);

lib/int_32.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class Int32 {
3434
* @ignore
3535
*/
3636
toExtendedJSON(options) {
37-
if (options && options.relaxed) return this.value;
37+
if (options && (options.relaxed || options.legacy)) return this.value;
3838
return { $numberInt: this.value.toString() };
3939
}
4040

lib/regexp.js

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,23 +39,40 @@ class BSONRegExp {
3939
}
4040
}
4141

42+
static parseOptions(options) {
43+
return options
44+
? options
45+
.split('')
46+
.sort()
47+
.join('')
48+
: '';
49+
}
50+
4251
/**
4352
* @ignore
4453
*/
45-
toExtendedJSON() {
54+
toExtendedJSON(options) {
55+
options = options || {};
56+
if (options.legacy) {
57+
return { $regex: this.pattern, $options: this.options };
58+
}
4659
return { $regularExpression: { pattern: this.pattern, options: this.options } };
4760
}
4861

4962
/**
5063
* @ignore
5164
*/
5265
static fromExtendedJSON(doc) {
66+
if (doc.$regex) {
67+
// This is for $regex query operators that have extended json values.
68+
if (doc.$regex._bsontype === 'BSONRegExp') {
69+
return doc;
70+
}
71+
return new BSONRegExp(doc.$regex, BSONRegExp.parseOptions(doc.$options));
72+
}
5373
return new BSONRegExp(
5474
doc.$regularExpression.pattern,
55-
doc.$regularExpression.options
56-
.split('')
57-
.sort()
58-
.join('')
75+
BSONRegExp.parseOptions(doc.$regularExpression.options)
5976
);
6077
}
6178
}

package-lock.json

Lines changed: 30 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)