Node.js module that is used to transform input - $ or $1 - jsons based on templates
const { getTransform } = require('metamorphosi');
const transform = getTransform(template, options);
transform($, $1);
template
a template is given as parameter. The possible values are described bellow in Template possible values
options?
an object containing
dropValues?
(default: [null, undefined, '']), Values that will be dropped from outputcb?
(default x => x) a tranformating function that will be used in all output values.
a function that when given two inputs will tranform them and produced 1 output based on the template
const {transform} = require('metamorphosi');
const transform(template, options)
The builder given is a convenient wrapper of the above method to easily reuse it with the same template and options.
template
a template is given as parameter. The possible values are described bellow in Template possible values
options
an object containing
dropValues?
(default: [null, undefined, '']), Values that will be dropped from outputcb?
(default x => x) a tranformating function that will be used in all output values.$
the input to be tranformed$1
a second input to be transformed
tranformed inputs based on the template and $, $1. The output type is very similar to the template given
const {getTransform} = require('metamorphosi');
/** A template example */
const template = {
simpleString:'string ',
simpleNumber: 100,
simpleBoolean: true,
simpleArray: [1,2,3],
path: '$.a.b.c',
'arrayTransformed[$.array]': { a: '$.b', index: '$.$index', parentValue: '$..a.b.c', array: '$.$array'},
simpleObject: {
pathFromAlternativeInput: '$1.a.b'
}
}
/** a cb example */
function cb(input){
if(typeof input ==='string') return input.trim();
return input;
}
/**
* The default dropValues used if not overwritten.
* - Any value in this array will be removed.
* - Empty arrays will be removed
* - Empty objects will also be removed
*/
const dropValues = [null, undefined, ''];
const transform = getTransform(template, {dropValues, cb})
> transform({a:{b:{c:' a value '}}, array: [{b: ' test '}, {b: 'another test'}]}, {a:{b:'alternative value '}})
/* {
"simpleString": "string",
"simpleNumber": 100,
"simpleBoolean": true,
"simpleArray": [1, 2, 3],
"path": "a value",
"arrayTransformed": [
{
"a": "test",
"index": 0,
"parentValue": "a value",
"array": [{"b": " test "}, {"b": "another test"}]
},
{
"a": "another test",
"index": 1,
"parentValue": "a value",
"array": [{"b": " test "}, {"b": "another test"}]
}
],
"simpleObject": {
"pathFromAlternativeInput": "alternative value"
}
}
*/
// This is the same as
const {transform} = require('template-transformer');
transform(template, {
dropValues,
cb,
$:{a:{b:{c:' a value '}}, array: [{b: ' test '},
$1:{b: 'another test'}
});
// getTrasform is usefull if you need to use trasform many times as it reuses
// the same template, options
As seen in the respective types the template can have 7 different type of values:
A literal number in the template will always be preserved in the resulting output
eg:
transform(1, {}); //1
unless it exists in dropValues
eg:
transform(0, { dropValues: [0] }); // undefined
Similarly a literal string will be always preserved. By default empty string are always dropped:
eg:
transform('foo', {}); // 'foo'
transform('', {}); // undefined
transform('', { dropValues: [] }); // ''
Similarly the use of boolean values are preserved no matter the input
eg:
transform(true, {}); // true
transform(false, {}); // false
With the use of $ or $1 you can access any value from the given json
eg:
transform('$.foo.bar', { $: { foo: { bar: 'value' } } }); // 'value'
// accessing arrays:
transform('$.foo.0.bar', { $: { foo: [{ bar: 'value' }] } }); // 'value'
All the above examples are pretty simple. Getting a literal value or a single value by accessing an input would be pretty simple without a module. The power of this module is with tranforming to objects. The values of a key can be any of the values of a template including another object. There are 5 differnt types of keys that can exist in an object template:
A simple string key
eg:
transform({ value: '$.foo' }, { $: { foo: 'bar' } }); // { value: 'bar' }
One common need is to iterate an inputs array and create a different array in the output.
eg:
transform({ 'value[$.foo]': '$.0' }, { $: { foo: ['bar', 'foo'] } }); // { value: [ 'b', 'f' ] }
There is a common need to create an array from 2 or more sources from the input json. To address this need you can use keys suffixed with .$1-9
eg:
transform(
{ 'value.$1': ['a', 'b'], 'value[$.foo].$2': '$.0', 'value.$3': '$.array' },
{ $: { foo: ['bar', 'foo'], array: ['c', 'd'] } }
); // { value: [ 'a', 'b', 'b', 'f', 'c', 'd' ] }
Note however that if a duplicate key value is not an array, this will overwrite the previous array with a non array
You can use a key value from the input
eg:
transform({ ['$.foo']: 'foo' }, { $: { foo: 'bar' } }); // { bar: 'foo' }
Sometimes you don't want an object to be included if some keys are missing. This is easily accomplished using the $if key
eg:
transform({ ['$.foo']: 'foo', a: '$.bar', $if: ['a'] }, { $: { foo: 'bar' } }); // undefined
transform({ ['$.foo']: 'foo', a: '$.bar', $if: ['a'] }, { $: { foo: 'bar', bar: 'bar' } }); // { bar: 'foo', a: 'bar' }
In addition to keys, the $\if array can contain functions. If at least one function returns false, the object won't be included in the final result.
eg:
transform({ ['$.foo']: 'foo', a: '$.bar', $if: [$ => false] }, { $: { foo: 'bar' } }); // undefined
transform({ ['$.foo']: 'foo', a: '$.bar', $if: [$ => $.bar.length > 0] }, { $: { foo: 'bar', bar: 'bar' } }); // { bar: 'foo', a: 'bar' }
transform({ ['$.foo']: 'foo', a: '$.bar', $if: [(_, $1) => !$1.erase] }, { $: { foo: 'bar', bar: 'bar' }, $1: { erase: true } }); // undefined
Sometimes you want an object to be included even if it is empty. By default metamorphosi will drop empty objects. If you want to keep empty objects you can use the $keep:true special key.
eg:
transform({ $keep:true }, { }); // {}
Keep can also be used inside arrays to ensure that if the array is empty after the transformation, it is not removed from the end result
eg:
transform({ a: ['$keep', undefined, null] }, { }); // {a: []}
As seen in some examples of object you can directly use arrays as deplates. The values of the array can be a valid template value
eg:
transform([1, '1', true, () => 'function', '$.foo', { foo: '$.foo' }, ['array']], { $: { foo: 'bar' } }); // [ 1, '1', true, 'function', 'bar', { foo: 'bar' } ]
You can also use callbacks that take as an input ($,$1)
eg:
transform($ => $.foo, { $: { foo: 'bar' } }); // 'bar'
All errors are cought by the library. Even in JSON paths (eg null pointer exceptions) or errors in Function callbacks. In cases an error occurs the template will resolve into an undefined value that will be removed (Unless dropValues is overwritten).
Sometimes it is useful to have a second maybe configuration object that can impact the output. An example can be when transforming an api response eg. $ and want to use some configuration eg. $1 to differentiate the output.
This work was inspired by jsonpath-object-transform and my work @Workable. More than 6 years of experience transforming jsons using templates and code resulted in this module. It tries to solve most common issues and difficulties people have when working with templates. This module concept was initially developed during my flight to Brussels from Athens and back.