stringifier ================================ Yet another stringify function. [![Build Status][travis-image]][travis-url] [![NPM package][npm-image]][npm-url] [![Bower package][bower-image]][bower-url] [![License][license-image]][license-url] DESCRIPTION --------------------------------------- `stringifier` is a function like `JSON.stringify` but intended to be more customizable. For example, - Max depth for recursive object tree traversal - Per-type output customization - Circular reference handling Pull-requests, issue reports and patches are always welcomed. `stringifier` is a spin-off product of [power-assert](https://github.com/power-assert-js/power-assert) project. API --------------------------------------- ### stringifier(options) `require('stringifier')` exports single function `stringifier` that accepts `options` as optional parameters and returns configured function for stringify. This is the comprehensive usage. ```javascript var stringifier = require('stringifier'); var stringify = stringifier(options); console.log(stringify(anyVar)); ``` ### stringifier.stringify(val, options) For more simplified usage, `stringifier` has a function `stringify`, that simply takes target object/value and returns stringified result string. `stringifier.stringify` accepts `options` as optional parameter too. ```javascript var stringify = require('stringifier').stringify; console.log(stringify(anyVar)); ``` INSTALL --------------------------------------- ### via npm Install $ npm install --save stringifier Use ```javascript var stringify = require('stringifier').stringify; console.log(stringify(anyVar)); ``` #### use stringifier npm module on browser `stringifier` function is exported ### via bower Install $ bower install --save stringifier Load (`stringifier` function is exported) Use ```javascript console.log(stringifier.stringify(anyVar)); ``` EXAMPLE --------------------------------------- For given context, ```javascript var stringifier = require('stringifier'), assert = require('assert'); function Student (name, age, gender) { this.name = name; this.age = age; this.gender = gender; } var AnonStudent = function(name, age, gender) { this.name = name; this.age = age; this.gender = gender; }; var student = new Student('tom', 10, 'M'); var anonStudent = new AnonStudent('mary', 9, 'F'); var values = [ 'string', [null, undefined], { primitives: [true, false, -5, 98.6], specific: { regex: /^not/, numbers: [NaN, Infinity, -Infinity] }, userDefined: [ student, anonStudent ] } ]; ``` #### default single-line output ```javascript var stringify = stringifier(); console.log(stringify(values)); ``` result: ```javascript ["string",[null,undefined],Object{primitives:[true,false,-5,98.6],specific:Object{regex:/^not/,numbers:[NaN,Infinity,-Infinity]},userDefined:[Student{name:"tom",age:10,gender:"M"},@Anonymous{name:"mary",age:9,gender:"F"}]}] ``` #### pretty printing with indentation Use `indent` option for pretty printing. Using four spaces for indentation in this case. ```javascript var stringify = stringifier({indent: ' '}); console.log(stringify(values)); ``` result: ```javascript [ "string", [ null, undefined ], Object{ primitives: [ true, false, -5, 98.6 ], specific: Object{ regex: /^not/, numbers: [ NaN, Infinity, -Infinity ] }, userDefined: [ Student{ name: "tom", age: 10, gender: "M" }, @Anonymous{ name: "mary", age: 9, gender: "F" } ] } ] ``` #### depth limitation Use `maxDepth` option to stringify at most specified levels. ```javascript var stringify = stringifier({maxDepth: 3, indent: ' '}); console.log(stringify(values)); ``` result: ```javascript [ "string", [ null, undefined ], Object{ primitives: [ true, false, -5, 98.6 ], specific: Object{ regex: /^not/, numbers: #Array# }, userDefined: [ #Student#, #@Anonymous# ] } ] ``` #### anonymous class label Use `anonymous` option to specify alternate type name for anonymous constructors. ```javascript var stringify = stringifier({anonymous: 'ANON'}); assert(stringify(anonStudent) === 'ANON{name:"mary",age:9,gender:"F"}'); ``` #### omit specific property from output Customize `options.handlers` ```javascript var stringify; // property whitelist and reordering stringify = stringifier({ handlers: { 'Student': s.object(null, ['gender', 'age']) } }); assert(stringify(student) === 'Student{gender:"M",age:10}'); // blacklist by property name stringify = stringifier({ handlers: { 'Student': s.object(function (kvp) { return ['age', 'gender'].indexOf(kvp.key) === -1; }) } }); assert(stringify(student) === 'Student{name:"tom"}'); // blacklist by property value stringify = stringifier({ handlers: { 'Student': s.object(function (kvp) { return kvp.value !== 'M'; }) } }); assert(stringify(student) === 'Student{name:"tom",age:10}'); // whitelist by property value stringify = stringifier({ handlers: { 'Student': s.object(function (kvp) { return typeName(kvp.value) === 'string'; }) } }); assert(stringify(student) === 'Student{name:"tom",gender:"M"}'); ``` #### truncate property value Return number from object predicate ```javascript stringify = stringifier({ handlers: { 'Student': s.object(function (kvp) { if (kvp.key === 'name') { return 3; } return true; }) } }); assert(stringify(student) === 'Student{name:"to..(snip),age:10,gender:"M"}'); ``` CONFIGURATION --------------------------------------- ### options #### options.maxDepth Type: `number` Default value: `null` Max depth for recursive Object tree traversal #### options.indent Type: `String` Default value: `null` string value for indentation. If this value is not empty, stringified result may contain multiple lines. #### options.lineSeparator Type: `String` Default value: `'\n'` string value for line-separator. Makes sense only if `options.indent` is not empty. #### options.anonymous Type: `String` Default value: `'@Anonymous'` Type name string alternative for displaying Object created by anonymous constructor #### options.circular Type: `String` Default value: `'#@Circular#'` Alternative string for displaying Circular reference #### options.snip Type: `String` Default value: `'..(snip)'` For displaying truncated string #### options.handlers `options.handlers` is a object where property names are type names (string, number, ...) and values are per-type stringify strategy functions. Various strategies are defined in `stringifier.strategies`, and default strategies are defined as follows. ```javascript var s = require('./strategies'); function defaultHandlers () { return { 'null': s.always('null'), 'undefined': s.always('undefined'), 'function': s.prune(), 'string': s.json(), 'boolean': s.json(), 'number': s.number(), 'symbol': s.toStr(), 'RegExp': s.toStr(), 'String': s.newLike(), 'Boolean': s.newLike(), 'Number': s.newLike(), 'Date': s.newLike(), 'Array': s.array(), 'Object': s.object(), 'Error': s.object(null, ['message', 'code']), '@default': s.object() }; } ``` If unknown type is detected, strategy function registered by `'@default'` key will be used. ### strategies For given `Student` pseudo-class and a `stringifier`, ```javascript var stringifier = require('stringifier'), s = stringifier.strategies, assert = require('assert'), function Student (name, age, gender) { this.name = name; this.age = age; this.gender = gender; } var student = new Student('tom', 10, 'M'); ``` #### always `always` strategy always returns passed constant (In this case, `'foo'`). ```javascript var stringify = stringifier({ handlers: { 'Student': s.always('foo') } }); assert(stringify(student) === 'foo'); ``` #### json `json` strategy applies `JSON.stringify` to input value then return the result string. ```javascript var stringify = stringifier({ handlers: { 'Student': s.json() } }); assert(stringify(student) === '{"name":"tom","age":10,"gender":"M"}'); ``` #### toStr `toStr` strategy calls `toString()` to input value then return the result string. ```javascript var stringify = stringifier({ handlers: { 'Student': s.toStr() } }); assert(stringify(student) === '[object Object]'); ``` #### prune `prune` strategy does not serialize target value but returns target type name surrounded by `#`. ```javascript var stringify = stringifier({ handlers: { 'Student': s.prune() } }); assert(stringify(student) === '#Student#'); ``` #### newLike `newLike` strategy emulates "new constructor call pattern". ```javascript var stringify = stringifier({ handlers: { 'Student': s.newLike() } }); assert(stringify(student) === 'new Student({"name":"tom","age":10,"gender":"M"})'); ``` #### object `object` strategy stringifies target object recursively and decorate object literal-like syntax with its type name. `object` is a default strategy for objects, and any other unknown types. ```javascript var stringify = stringifier({ handlers: { 'Student': s.object() } }); assert(stringify(student) === 'Student{name:"tom",age:10,gender:"M"}'); ``` #### array `array` strategy is an array specific stringification strategy, and is a default strategy for arrays. ```javascript var stringify = stringifier({ handlers: { 'Array': s.array() } }); assert(stringify(['foo', 'bar', 'baz']) === '["foo","bar","baz"]'); ``` #### number `number` strategy is a number specific stringification strategy, and is a default strategy for number. `number` strategy also provides `NaN`,`Infinity` and `-Infinity` handling. ```javascript var stringify = stringifier({ handlers: { 'Array': s.array(), 'number': s.number() } }); assert(stringify([NaN, 0, Infinity, -0, -Infinity]) === '[NaN,0,Infinity,0,-Infinity]'); ``` AUTHOR --------------------------------------- * [Takuto Wada](https://github.com/twada) LICENSE --------------------------------------- Licensed under the [MIT](https://twada.mit-license.org/2014-2018) license. [travis-url]: https://travis-ci.org/twada/stringifier [travis-image]: https://secure.travis-ci.org/twada/stringifier.svg?branch=master [npm-url]: https://npmjs.org/package/stringifier [npm-image]: https://badge.fury.io/js/stringifier.svg [bower-url]: https://badge.fury.io/bo/stringifier [bower-image]: https://badge.fury.io/bo/stringifier.svg [license-url]: https://twada.mit-license.org/2014-2018 [license-image]: https://img.shields.io/badge/license-MIT-brightgreen.svg