0.7.2 • Published 5 years ago

@kowarschick/json-transformer v0.7.2

Weekly downloads
-
License
MIT
Repository
gitlab
Last release
5 years ago

license pipeline status pipeline status

A modular JSON tranformer package

The @kowarschick/json-transformer package is used to transform JSON template objects into JSON objects. It has been developed in course of the lecture “Web Programming” by Wolfgang L. J. Kowarschick.

This package can be used in all cases where an application is initialized using a JSON file. The content of such file can be transformed in a variety of ways. Typical applications are REST API templates, game levels, or configuration files.

Please note that I use the Horstmann Brace Style. Every LISP programmer knows why. Moverover, since I like to make code readable manually, I don't use JavaScript Standard Style.

Installation

npm install --save @kowarschick/json-transformer

Usage

Objects of the class JsonTransformer transform JSON objects by means of the method transform. The JsonTransformerSome, which is a subclass of JsonTransformer, e.g., returns randomly an element of an array starting with $some. The head element $some is, of course, never returned.

Import { JsonTransformerSome } from '@kowarschick/json-transformer';

const transformer =
  new JsonTransformerSome();

transformer.transform({ value: ["$some", 5, 7, 9] })
// => 5 or 7 or 9

There are several transformers and transformer functions. Transformer functions are a little somewhat simpler than stand-alone tranformers. They can be passed to the JsonTransformerFunction transformer, which applies them when appropriate. The example above could also be implemented as follows:

Import { JsonTransformerFunction } from '@kowarschick/json-transformer';
Import { JsonFunctionSome }        from '@kowarschick/json-transformer';

const transformer =
  new JsonTransformerFunction({ init: [JsonFunctionSome] });

transformer.transform({ value: ["$some", 5, 7, 9] })
// => 5 or 7 or 9

Every transformer function can also be implemented as stand-alone transformer. The contrary, however, is not true: More complex transformers, such as JsonTransformerTraversal (see below), cannot be realized as transformer functions.

Transformers can be connected to more complex transformers by means of the pipe method. For instance, the traversal transformer JsonTransformerTraversal recursively traverses a JSON object and applies the transformers of the pipe to each element.

In the next example there is a third transformer used: The level transformer JsonTransformerLevel replaces the string "$level" by the level of the JSON container which it is a member of.

Import { JsonTransformerTraversal } from '@kowarschick/json-transformer';
Import { JsonTransformerLevel }     from '@kowarschick/json-transformer';
Import { JsonFunctionSome }         from '@kowarschick/json-transformer';

const
  transformer =
         new JsonTransformerTraversal()
   .pipe(new JsonTransformerLevel())
   .pipe(new JsonTransformerSome());

transformer.transform
({ value: ["$some", "$level", ["$level"], [["$level"]] })
// => 1 or [2] or [[3]]

REST API Example

One area where JSON transformers are useful are REST APIs. Before delivering JSON data to a client you can define approriate JSON templates. Those templates can be transformed to the JSON to be delivered, by using the transformers JsonTransformerTraversal and JsonTransformerTemplateFunctions.

The first transformer has already been introduced, the second transformer tests whether the current element is a string. If so, it looks into the data object (which may be passed either to the transformer itself as default data object or to the method transform as current data object) whether there are appropiate replacement values for specific string templates (${...}). If such a replacement value is found, the string template (not the whole string) is replaced by the value found in the data object.

In contrast to JsonTransformerTemplate the JsonTransformerTemplateFunctions transformer also supports string templates with function calls: ${...(...)}. If such a template is found and a suitable function is found in the data object that function is invoked to compute the replacement value.

Import { JsonTransformerTraversal }         from '@kowarschick/json-transformer';
Import { JsonTransformerTemplateFunctions } from '@kowarschick/json-transformer';

const
  transformer =
         new JsonTransformerTraversal()
   .pipe(new JsonTransformerTemplateFunctions()),
  
  restTemplate =
  { "links":
    { "self":   "${base}/articles",
      "parent": "${base}"
    },
  
    "data": "${articles()}"
  },
  
  data =
  { base:     "https://www.example.com/v1",
    articles: () =>
              [ { id: 1, type: "articels", title: "Apple"  },
                { id: 2, type: "articles", title: "Orange" },
              ]
              // usually computed by calling some database service
  };

console.log
( transformer.transform({ value: restTemplate, data: data }) );

// =>
// { "links":
//   { "self":   "https://www.example.com/v1/articles",
//     "parent": "https://www.example.com/v1"
//   },
//   "data":
//   [ { "id": 1, type: "articels", title: "Apple" },
//     { "id": 2, type: "articles", title: "Orange" }
//   ]
// }

Complex Game Level Example

A game level, e.g., often is described by a JSON file. Many elements of a level are usually created randomly, the size of the level may depend on the size of the browser window etc.

Below is a slightly more complex example: The JSON transformer package is used to shuffle the tokens of a memory game (Pairs).

JsonTransformerStringReplace replaces certain strings by values found in the data object. It is less complex and more performant than JsonTransformerTemplate as it does not support string templates. It only replaces complete strings by other values.

import { JsonTransformerTraversal }     from '@kowarschick/json-transformer';
import { JsonTransformerFunction }      from '@kowarschick/json-transformer';
import { JsonTransformerStringReplace } from '@kowarschick/json-transformer';

import { JsonFunctionDuplicate }        from '@kowarschick/json-transformer';
import { JsonFunctionSequence }         from '@kowarschick/json-transformer';
import { JsonFunctionShuffle }          from '@kowarschick/json-transformer';
import { JsonFunctionUnnest }           from '@kowarschick/json-transformer';

const
  transformer =  
          new JsonTransformerTraversal
              ({ data:
                 { "@noOfPairs": 10,
                   "@image":     i => 'image'+i
                 }
              })
    .pipe(new JsonTransformerFunction
              ({ init:
                 [ JsonFunctionDuplicate,
                   JsonFunctionSequence,
                   JsonFunctionShuffle,
                   JsonFunctionUnnest,
                 ]
              })
         )
    .pipe(new JsonTransformerStringReplace()),

  pairs =
  { cards: { "$function": "$sequence",
             "$first":    1,
             "$last":     "@noOfPairs",
             "$format":   "@image"  
           },
    board: { "$function": "$shuffle",
             "$value":    { "$function": "$duplicate",
                            "$value":    { "$function": "$sequence",
                                           "$first":    1,
                                           "$last":     "@noOfPairs"
                                         },
                            "$times":    2,
                            "$flatten":  true
                          }
           }
  };

console.log(transformer.transform({ value: pairs }));
// =>
// { cards:
//   [ 'image1', 'image2', 'image3', 'image4',
//     'image5', 'image6', 'image7', 'image8',
//     'image9', 'image10'
//   ],
//
//   board:
//   [  6,  8, 7, 5, 6, 7, 5,
//      2,  2, 8, 4, 9, 9, 3,
//     10, 10, 1, 1, 4, 3
//   ]
// }

console.log
( transformer.transform
  ({ value: pairs,
     data:  { "@noOfPairs": 4 }
  })
);
// =>
// { cards: [ 'image1', 'image2', 'image3', 'image4' ],
//   board: [ 1, 4, 2, 3, 3, 4, 1, 2 ]
// }

console.log
( transformer.transform
  ({ value: pairs,
     data:  
     { "@noOfPairs": 20,
       "@image":     i => 'bild'+('__'+i).slice(-3)
     }
  })
);
// =>
// { cards:
//   [ 'bild__1', 'bild__2', 'bild__3', 'bild__4',
//     'bild__5', 'bild__6', 'bild__7', 'bild__8',
//     'bild__9', 'bild_10', 'bild_11', 'bild_12',
//     'bild_13', 'bild_14', 'bild_15', 'bild_16',
//     'bild_17', 'bild_18', 'bild_19', 'bild_20'
//   ],

//   board:
//   [ 5,  3, 14, 17, 18, 17,  5, 16, 11, 10,
//     9, 12, 11, 12, 15,  8,  1, 15, 14,  4,
//     2,  2,  6, 18,  4, 10, 16,  3,  6,  7,
//     7, 13, 19, 20, 13,  1,  9,  8, 20, 19
//   ]
// }

More Examples

See directories examples_cjs, examples_es6, and test for many more examples.

Transformers

JsonTransformer:

  • The transformer itself does not perform any transformations.
  • It is used mainly as a superclass for other transformers.

JsonTransformerTraversal:

  • The transformer itself does not perform any transformations.
  • It recursively applies all transformers of the transformer pipe to all atom elements of the JSON value to be transformed.

JsonTransformerTraversalBreadthFirst:

  • The transformer itself does not perform any transformations.
  • It recursively applies all transformers of the transformer pipe in a breadth-first ordering to all atom elements of the JSON value to be transformed.

JsonTransformerTraversalRestricted:

  • The transformer itself does not perform any transformations.
  • It recursively applies all transformers of the transformer pipe to atom elements on certain levels within the JSON value to be transformed.

JsonTransformerFunction:

  • The transformer itself does not perform any transformations.
  • The programmer, however, can pass a bunch of JSON function descriptors to the constructor.
  • A JSON array that starts with a string described by one of those descriptors is considered to be a function call. The function to which that array is passed as input is also a member of the descriptor.
  • A JSON function call can just as well be initiated by objects that cointain two attributes $function and $value.
  • Function call objects can have additional attributes. So every function call descriptor of type JsonType.Array also defines a function call descriptor of type JsonType.Object, but not vice versa!
  • Function call descriptors of other types such as JsonType.String, JsonType.Null etc. are also possible, but are rare.
  • To transform values that are passed to as JASON function before it is called, a traversal transformer such as JsonTransformerTraversal has to be used.
  • function/aggregate:
    • This module contains several JSON function descriptors to aggregate the values passed as arguments to the function:
      • JsonFunctionMin: minimal value (number)
      • JsonFunctionMax: maximal value (number)
      • JsonFunctionMinString: minimal value (string)
      • JsonFunctionMaxString: maximal value (string)
      • JsonFunctionSum: sum of all values (number)
      • JsonFunctionProduct: product of all values (number)
      • JsonFunctionAverage: average of all values (number)
    • Many more aggregate descriptors can be defined by the programmer herself (analogous to Array.prototype.reduce).
  • JsonFunctionCount (function/count):
    • The number of values passed to the array.
  • JsonFunctionDuplicate (function/duplicate):
    • Duplicates the values passed as values to the function several times.
  • JsonFunctionLevel (function/level):
    • A function call descriptor of type JsonType.String. The string $level is replace by its level within the JSON value passed to the to level transformer.
    • This JSON function usually is used for testing purposes only, for instance, if the transformer JsonTransformerTraversalRestricted is to be used.
  • JsonFunctionRandom (function/random):
    • Computes random integer and float numbers.
  • JsonFunctionSequence (function/sequence):
    • Returns an array with a sequence of numbers, which additionally may be formatted.
  • JsonFunctionShuffle (function/shuffle):
    • Shuffles the values passed to the function.
  • JsonFunctionSome (function/some):
    • Arbitrarily returns one of the values passed to the function.
  • JsonFunctionUnnest (function/unnest):
    • Flattens an array.

JsonTransformerFunctionTraversal:

  • Very important, but still missing.
  • To be implemented in Version 0.8.x.

JsonTransformerLevel:

  • A transformer that does the very same JsonFunctionLevel (when passed to the transformer JsonTransformerFunction).
  • Usually JsonFunctionLevel should be preferred.
  • This transformer can be used as an example for a string transformer.

JsonTransformerNull:

  • A transformer which replaces undefined, whcih is no JSON value, by null, which is a JSON value.
  • This transformer can be used as an example for a null transformer.

JsonTransformerRandom:

  • A transformer that does the very same JsonFunctionRandom (when passed to the transformer JsonTransformerFunction).
  • Usually JsonFunctionRandom should be preferred.
  • This transformer can be used as an example for an object transformer.

JsonTransformerSome:

  • A transformer that does the very same JsonFunctionSome (when passed to the transformer JsonTransformerFunction).
  • Usually JsonFunctionSome should be preferred.
  • This transformer can be used as an example for an array transformer.

JsonTransformerStringReplace:

  • A transformer that replaces strings by values found in the data object (passed to the transformer or the transform method.)

JsonTransformerTemplate:

  • Replaces string templates of kind ${name} by values found in the data object.
  • This may a very expensive transformer, if it used together with JsonTransformerTraversal, as in this case every string within a JSON value is parsed from the first to the last character. A better alternative is still to be implemented.

JsonTransformerTemplateFunction:

  • Replaces string templates of kind ...${name}... by values found in the data object.
  • String templates of kind ...${name(arg1, ..., arg5)}... are replaced by the results of associated function calls.
  • This may a very expensive transformer, if it used together with JsonTransformerTraversal, as in this case every string within a JSON value is parsed from the first to the last character. A better alternative is still to be implemented.

Releases

0.7.0

  • The functionality has not changed.
  • The repository has changed (Github → Gitlab).
  • eslint has been added.
  • The test coverage has been improved.

0.6.3

  • Initial release.

Roadmap

0.8.0

  • JsonTransformerFunctionTraversal
  • Associated more complex JSON functions.

0.9.0

  • Lazy transformers, i.e. Transformers that replace JSON values with functions that only calculate the resulting JSON value when they are called.

License

The MIT License (MIT, https://opensource.org/licenses/MIT)

Copyright © 2020 Wolfgang L. J. Kowarschick

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

0.7.2

5 years ago

0.6.3

5 years ago