Add JSONStringPacker

Add a helper for encoding JavaScript values into JSON String format.

The implementation is based on JSON String format
and preserves JavaScript objects like Buffers
and Dates, as opposed to encoding format.
This commit is contained in:
Masu Lin 2016-10-29 00:20:34 +08:00
parent b698b82ce7
commit d45aaec5ef
2 changed files with 166 additions and 0 deletions

94
lib/json-string-packer.js Normal file
View File

@ -0,0 +1,94 @@
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
// Node module: loopback-connector
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
var createPromiseCallback = require('./utils').createPromiseCallback;
module.exports = JSONStringPacker;
var ISO_DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*))(?:Z|(\+|-)([\d|:]*))?$/;
/**
* Create a new Packer instance that can be used to convert between JavaScript
* objects and a JsonString representation in a String.
*
* @param {String} encoding Buffer encoding refer to https://nodejs.org/api/buffer.html#buffer_buffers_and_character_encodings
*/
function JSONStringPacker(encoding) {
this.encoding = encoding || 'base64';
}
/**
* Encode the provided value to a `JsonString`.
*
* @param {*} value Any value (string, number, object)
* @callback {Function} cb The callback to receive the parsed result.
* @param {Error} err
* @param {Buffer} data The encoded value
* @promise
*/
JSONStringPacker.prototype.encode = function(value, cb) {
var encoding = this.encoding;
cb = cb || createPromiseCallback();
try {
var data = JSON.stringify(value, function(key, value) {
if (Buffer.isBuffer(this[key])) {
return {
type: 'Buffer',
data: this[key].toString(encoding),
};
} else {
return value;
}
});
setImmediate(function() {
cb(null, data);
});
} catch (err) {
setImmediate(function() {
cb(err);
});
}
return cb.promise;
};
/**
* Decode the JsonString value back to a JavaScript value.
* @param {String} jsonString The JsonString input.
* @callback {Function} cb The callback to receive the composed value.
* @param {Error} err
* @param {*} value Decoded value.
* @promise
*/
JSONStringPacker.prototype.decode = function(jsonString, cb) {
var encoding = this.encoding;
cb = cb || createPromiseCallback();
try {
var value = JSON.parse(jsonString, function(k, v) {
if (v && v.type && v.type === 'Buffer') {
return new Buffer(v.data, encoding);
}
if (ISO_DATE_REGEXP.exec(v)) {
return new Date(v);
}
return v;
});
setImmediate(function() {
cb(null, value);
});
} catch (err) {
setImmediate(function() {
cb(err);
});
}
return cb.promise;
};

View File

@ -0,0 +1,72 @@
// Copyright IBM Corp. 2016. All Rights Reserved.
// Node module: loopback-connector
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
var JSONStringPacker = require('../lib/json-string-packer');
var expect = require('chai').expect;
describe('JSONStringPacker', function() {
var packer;
beforeEach(function createPacker() {
packer = new JSONStringPacker();
});
describe('encode()', function() {
it('supports invocation with a callback', function(done) {
packer.encode('a-value', done);
});
});
describe('decode()', function() {
it('supports invocation with a callback', function(done) {
packer.encode('a-value', function(err, jsonString) {
if (err) return done(err);
packer.decode(jsonString, function(err, result) {
if (err) return done(err);
expect(result).to.eql('a-value');
done();
});
});
});
});
describe('roundtrip', function() {
var TEST_CASES = {
String: 'a-value',
Object: { a: 1, b: 2 },
Buffer: new Buffer([1, 2, 3]),
Date: new Date('2016-08-03T11:53:03.470Z'),
Integer: 12345,
Float: 12.345,
Boolean: false,
};
Object.keys(TEST_CASES).forEach(function(tc) {
it('works for ' + tc + ' values', function() {
var value = TEST_CASES[tc];
return encodeAndDecode(value)
.then(function(result) {
expect(result).to.eql(value);
});
});
});
it('works for nested properties', function() {
return encodeAndDecode(TEST_CASES)
.then(function(result) {
expect(result).to.eql(TEST_CASES);
});
});
function encodeAndDecode(value) {
return packer.encode(value)
.then(function(binary) {
return packer.decode(binary);
});
}
});
});