You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					171 lines
				
				6.0 KiB
			
		
		
			
		
	
	
					171 lines
				
				6.0 KiB
			| 
											3 years ago
										 | # fast-safe-stringify
 | ||
|  | 
 | ||
|  | Safe and fast serialization alternative to [JSON.stringify][]. | ||
|  | 
 | ||
|  | Gracefully handles circular structures instead of throwing in most cases. | ||
|  | It could return an error string if the circular object is too complex to analyze, | ||
|  | e.g. in case there are proxies involved. | ||
|  | 
 | ||
|  | Provides a deterministic ("stable") version as well that will also gracefully | ||
|  | handle circular structures. See the example below for further information. | ||
|  | 
 | ||
|  | ## Usage
 | ||
|  | 
 | ||
|  | The same as [JSON.stringify][]. | ||
|  | 
 | ||
|  | `stringify(value[, replacer[, space[, options]]])` | ||
|  | 
 | ||
|  | ```js | ||
|  | const safeStringify = require('fast-safe-stringify') | ||
|  | const o = { a: 1 } | ||
|  | o.o = o | ||
|  | 
 | ||
|  | console.log(safeStringify(o)) | ||
|  | // '{"a":1,"o":"[Circular]"}' | ||
|  | console.log(JSON.stringify(o)) | ||
|  | // TypeError: Converting circular structure to JSON | ||
|  | 
 | ||
|  | function replacer(key, value) { | ||
|  |   console.log('Key:', JSON.stringify(key), 'Value:', JSON.stringify(value)) | ||
|  |   // Remove the circular structure | ||
|  |   if (value === '[Circular]') { | ||
|  |     return | ||
|  |   } | ||
|  |   return value | ||
|  | } | ||
|  | 
 | ||
|  | // those are also defaults limits when no options object is passed into safeStringify | ||
|  | // configure it to lower the limit. | ||
|  | const options = { | ||
|  |   depthLimit: Number.MAX_SAFE_INTEGER, | ||
|  |   edgesLimit: Number.MAX_SAFE_INTEGER | ||
|  | }; | ||
|  | 
 | ||
|  | const serialized = safeStringify(o, replacer, 2, options) | ||
|  | // Key: "" Value: {"a":1,"o":"[Circular]"} | ||
|  | // Key: "a" Value: 1 | ||
|  | // Key: "o" Value: "[Circular]" | ||
|  | console.log(serialized) | ||
|  | // { | ||
|  | //  "a": 1 | ||
|  | // } | ||
|  | ``` | ||
|  | 
 | ||
|  | 
 | ||
|  | Using the deterministic version also works the same: | ||
|  | 
 | ||
|  | ```js | ||
|  | const safeStringify = require('fast-safe-stringify') | ||
|  | const o = { b: 1, a: 0 } | ||
|  | o.o = o | ||
|  | 
 | ||
|  | console.log(safeStringify(o)) | ||
|  | // '{"b":1,"a":0,"o":"[Circular]"}' | ||
|  | console.log(safeStringify.stableStringify(o)) | ||
|  | // '{"a":0,"b":1,"o":"[Circular]"}' | ||
|  | console.log(JSON.stringify(o)) | ||
|  | // TypeError: Converting circular structure to JSON | ||
|  | ``` | ||
|  | 
 | ||
|  | A faster and side-effect free implementation is available in the | ||
|  | [safe-stable-stringify][] module. However it is still considered experimental | ||
|  | due to a new and more complex implementation. | ||
|  | 
 | ||
|  | ### Replace strings constants
 | ||
|  | 
 | ||
|  | - `[Circular]` - when same reference is found | ||
|  | - `[...]` - when some limit from options object is reached | ||
|  | 
 | ||
|  | ## Differences to JSON.stringify
 | ||
|  | 
 | ||
|  | In general the behavior is identical to [JSON.stringify][]. The [`replacer`][] | ||
|  | and [`space`][] options are also available. | ||
|  | 
 | ||
|  | A few exceptions exist to [JSON.stringify][] while using [`toJSON`][] or | ||
|  | [`replacer`][]: | ||
|  | 
 | ||
|  | ### Regular safe stringify
 | ||
|  | 
 | ||
|  | - Manipulating a circular structure of the passed in value in a `toJSON` or the | ||
|  |   `replacer` is not possible! It is possible for any other value and property. | ||
|  | 
 | ||
|  | - In case a circular structure is detected and the [`replacer`][] is used it | ||
|  |   will receive the string `[Circular]` as the argument instead of the circular | ||
|  |   object itself. | ||
|  | 
 | ||
|  | ### Deterministic ("stable") safe stringify
 | ||
|  | 
 | ||
|  | - Manipulating the input object either in a [`toJSON`][] or the [`replacer`][] | ||
|  |   function will not have any effect on the output. The output entirely relies on | ||
|  |   the shape the input value had at the point passed to the stringify function! | ||
|  | 
 | ||
|  | - In case a circular structure is detected and the [`replacer`][] is used it | ||
|  |   will receive the string `[Circular]` as the argument instead of the circular | ||
|  |   object itself. | ||
|  | 
 | ||
|  | A side effect free variation without these limitations can be found as well | ||
|  | ([`safe-stable-stringify`][]). It is also faster than the current | ||
|  | implementation. It is still considered experimental due to a new and more | ||
|  | complex implementation. | ||
|  | 
 | ||
|  | ## Benchmarks
 | ||
|  | 
 | ||
|  | Although not JSON, the Node.js `util.inspect` method can be used for similar | ||
|  | purposes (e.g. logging) and also handles circular references. | ||
|  | 
 | ||
|  | Here we compare `fast-safe-stringify` with some alternatives: | ||
|  | (Lenovo T450s with a i7-5600U CPU using Node.js 8.9.4) | ||
|  | 
 | ||
|  | ```md | ||
|  | fast-safe-stringify:   simple object x 1,121,497 ops/sec ±0.75% (97 runs sampled) | ||
|  | fast-safe-stringify:   circular      x 560,126 ops/sec ±0.64% (96 runs sampled) | ||
|  | fast-safe-stringify:   deep          x 32,472 ops/sec ±0.57% (95 runs sampled) | ||
|  | fast-safe-stringify:   deep circular x 32,513 ops/sec ±0.80% (92 runs sampled) | ||
|  | 
 | ||
|  | util.inspect:          simple object x 272,837 ops/sec ±1.48% (90 runs sampled) | ||
|  | util.inspect:          circular      x 116,896 ops/sec ±1.19% (95 runs sampled) | ||
|  | util.inspect:          deep          x 19,382 ops/sec ±0.66% (92 runs sampled) | ||
|  | util.inspect:          deep circular x 18,717 ops/sec ±0.63% (96 runs sampled) | ||
|  | 
 | ||
|  | json-stringify-safe:   simple object x 233,621 ops/sec ±0.97% (94 runs sampled) | ||
|  | json-stringify-safe:   circular      x 110,409 ops/sec ±1.85% (95 runs sampled) | ||
|  | json-stringify-safe:   deep          x 8,705 ops/sec ±0.87% (96 runs sampled) | ||
|  | json-stringify-safe:   deep circular x 8,336 ops/sec ±2.20% (93 runs sampled) | ||
|  | ``` | ||
|  | 
 | ||
|  | For stable stringify comparisons, see the performance benchmarks in the | ||
|  | [`safe-stable-stringify`][] readme. | ||
|  | 
 | ||
|  | ## Protip
 | ||
|  | 
 | ||
|  | Whether `fast-safe-stringify` or alternatives are used: if the use case | ||
|  | consists of deeply nested objects without circular references the following | ||
|  | pattern will give best results. | ||
|  | Shallow or one level nested objects on the other hand will slow down with it. | ||
|  | It is entirely dependant on the use case. | ||
|  | 
 | ||
|  | ```js | ||
|  | const stringify = require('fast-safe-stringify') | ||
|  | 
 | ||
|  | function tryJSONStringify (obj) { | ||
|  |   try { return JSON.stringify(obj) } catch (_) {} | ||
|  | } | ||
|  | 
 | ||
|  | const serializedString = tryJSONStringify(deep) || stringify(deep) | ||
|  | ``` | ||
|  | 
 | ||
|  | ## Acknowledgements
 | ||
|  | 
 | ||
|  | Sponsored by [nearForm](http://nearform.com) | ||
|  | 
 | ||
|  | ## License
 | ||
|  | 
 | ||
|  | MIT | ||
|  | 
 | ||
|  | [`replacer`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#The%20replacer%20parameter | ||
|  | [`safe-stable-stringify`]: https://github.com/BridgeAR/safe-stable-stringify | ||
|  | [`space`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#The%20space%20argument | ||
|  | [`toJSON`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#toJSON()_behavior | ||
|  | [benchmark]: https://github.com/epoberezkin/fast-json-stable-stringify/blob/67f688f7441010cfef91a6147280cc501701e83b/benchmark | ||
|  | [JSON.stringify]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify |