Skip to content
This repository has been archived by the owner on Jun 7, 2019. It is now read-only.

Commit

Permalink
Allow custom JSON revivers and replacers
Browse files Browse the repository at this point in the history
The JSON converter may be extended to provide a custom reviver and/or
replacer. The custom converter must then be registered into a MIME
registry before it can be consumed by the MIME interceptor.

This CL also introduced delegate converters within a registry. A delegate
is a dynamically bound converter that proxies the read and write methods
to the target.

The '+json' converter now delegates to the 'application/json' converter.

Fixes: #50
  • Loading branch information
scothis committed Jan 12, 2015
1 parent 0d46566 commit 405cbc3
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 12 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ Change Log
- support for Safari 8, iOS 8.0 and 8.1 (no code changes required, now actively testing)
- raw configuration objects are retained by interceptors, config objects are no longer begotten
- transient timeouts via config.transient on rest/interceptor/timeout, allows retry interceptor to wrap timeout
- extend JSON converter for custom reviver and replacers
- request.mixin properties attempt setting before before and after opening the request. Some browsers (IE) are sensitive to when the properties are set.
- wire.js rest factory interceptors now wire configuration objects
- normalize responses for linked and embedded resources from application/hal mime converter to always be a ResponsePromise
Expand Down
19 changes: 18 additions & 1 deletion docs/mime.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,26 @@ Converters need to be registered for a MIME type. MIME types are well known and
Vendor MIMEs '\*/vnd.*' are unreserved and available for internal use and experimentation. Application may use vendor MIMEs to provide additional knowledge about the type of object an entity represents, or to vary the rendering of the same object. As an example, 'application/json' provides a strong structure for the entity, but does nothing to indicate what the entity represents. Vendor MIMEs can provide that extra semantic meaning, but requires both the client and server understand and establish that meaning out-of-band.


<a name="custom-json-converters"></a>
## Custom JSON Converters

Advanced JSON serialization often leverages custom revivers and replacers. The JSON converter can be extended to use custom revivers and replacers.

```javascript
var json = require('rest/mime/type/application/json');
var customJson = json.extend(reviver, replacer);
```

In order to use the extended converter, it must be [registered in a MIME registry](#mime-converters-custom). It's generally recommended to use a custom vendored MIME type when overriding a common converter. If that is not possible, using a child registry is highly recommended to avoid conflicts.

```javascript
registry.register('application/json', customJson);
```


<a name="mime-interceptor"></a>
## MIME Interceptor

`rest/interceptor/mime` ([src](../interceptor/mime.js))

The [MIME interceptor](interceptors.md#module-rest/interceptor/mime) utilizes the mime registry to convert request and response entities between objects and text strings.
The [MIME interceptor](interceptors.md#module-rest/interceptor/mime) utilizes the mime registry to convert request and response entities between objects and text strings. A custom registry
28 changes: 27 additions & 1 deletion mime/registry.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,32 @@
return when.reject(new Error('Unable to locate converter for mime "' + parsed.raw + '"'));
};

/**
* Create a late dispatched proxy to the target converter.
*
* Common when a converter is registered under multiple names and
* should be kept in sync if updated.
*
* @param {string} type mime converter to dispatch to
* @returns converter whose read/write methods target the desired mime converter
*/
this.delegate = function delegate(type) {
return {
read: function () {
var args = arguments;
return this.lookup(type).then(function (converter) {
return converter.read.apply(this, args);
}.bind(this));
}.bind(this),
write: function () {
var args = arguments;
return this.lookup(type).then(function (converter) {
return converter.write.apply(this, args);
}.bind(this));
}.bind(this)
};
};

/**
* Register a custom converter for a MIME type
*
Expand Down Expand Up @@ -77,7 +103,7 @@
registry.register('multipart/form-data', require('./type/multipart/form-data'));
registry.register('text/plain', require('./type/text/plain'));

registry.register('+json', registry.lookup('application/json'));
registry.register('+json', registry.delegate('application/json'));

return registry;

Expand Down
36 changes: 27 additions & 9 deletions mime/type/application/json.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012 the original author or authors
* Copyright 2012-2015 the original author or authors
* @license MIT, see LICENSE.txt for details
*
* @author Scott Andrews
Expand All @@ -10,17 +10,35 @@

define(function (/* require */) {

return {
/**
* Create a new JSON converter with custom reviver/replacer.
*
* The extended converter must be published to a MIME registry in order
* to be used. The existing converter will not be modified.
*
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON
*
* @param {function} [reviver=undefined] custom JSON.parse reviver
* @param {function|Array} [replacer=undefined] custom JSON.stringify replacer
*/
function createConverter(reviver, replacer) {
return {

read: function (str) {
return JSON.parse(str);
},
read: function (str) {
return JSON.parse(str, reviver);
},

write: function (obj) {
return JSON.stringify(obj);
}
write: function (obj) {
return JSON.stringify(obj, replacer);
},

extend: createConverter

};
}

return createConverter();

};
});

}(
Expand Down
16 changes: 16 additions & 0 deletions test/mime/registry-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,22 @@
return registry.lookup('application/vnd.com.example+foo').then(function (c) {
assert.same(converter, c);
}).otherwise(fail);
},
'should invoke the delegate mime converter': function () {
var converter = {
read: function (obj) {
return 'read ' + obj;
},
write: function (obj) {
return 'write ' + obj;
}
};
registry.register('+bar', registry.delegate('+foo'));
registry.register('+foo', converter);
return registry.lookup('application/vnd.com.example+foo').then(function (converter) {
assert.same('read hello', converter.read('hello'));
assert.same('write world', converter.write('world'));
});
}
});

Expand Down
16 changes: 15 additions & 1 deletion test/mime/type/application/json-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
(function (buster, define) {
'use strict';

var assert, refute;
var assert, refute, undef;

assert = buster.assertions.assert;
refute = buster.assertions.refute;
Expand All @@ -23,6 +23,20 @@
},
'should stringify json': function () {
assert.equals('{"foo":"bar"}', json.write({ foo: 'bar' }));
},
'should use provided reviver and replacer': function () {
var reviver, replacer, customJson;

reviver = function reviver() {};
replacer = [];
customJson = json.extend(reviver, replacer);

assert.equals(undef, customJson.read('{"foo":"bar"}'));
assert.equals('{}', customJson.write({ foo: 'bar' }));

// old json convert is unmodified
assert.equals({ foo: 'bar' }, json.read('{"foo":"bar"}'));
assert.equals('{"foo":"bar"}', json.write({ foo: 'bar' }));
}
});

Expand Down

0 comments on commit 405cbc3

Please sign in to comment.