Description
Prevent `JSON.serialize()` from throwing, changing types, filtering or transforming values unexpectedly.
safe-json-value alternatives and similar modules
Based on the "Parsing" category.
Alternatively, view safe-json-value alternatives based on common mentions on social networks and blogs.
-
markdown-it
Markdown parser, done right. 100% CommonMark support, extensions, syntax plugins & high speed -
parse5
HTML parsing/serialization toolset for Node.js. WHATWG HTML Living Standard (aka HTML5)-compliant. -
fast-xml-parser
Validate XML, Parse XML and Build XML rapidly without C/C++ based libraries and no callback. -
xlsx-populate
Excel XLSX parser/generator written in JavaScript with Node.js and browser support, jQuery/d3-style method chaining, encryption, and a focus on keeping existing workbook features and styles in tact. -
json-query
DISCONTINUED. Retrieves values from JSON objects for data binding [GET https://api.github.com/repos/auditassistant/json-query: 404 - Not Found // See: https://docs.github.com/rest/repos/repos#get-a-repository]
CodeRabbit: AI Code Reviews for Developers

* Code Quality Rankings and insights are calculated and provided by Lumnify.
They vary from L1 to L5 with "L5" being the highest.
Do you think we are missing an alternative of safe-json-value or a related project?
README
⛑️ JSON serialization should never fail.
Features
Prevent JSON.stringify()
from:
- Throwing
- Changing types
- Filtering or transforming values unexpectedly
Example
<!-- eslint-disable fp/no-mutation -->
import safeJsonValue from 'safe-json-value'
const input = { one: true }
input.self = input
JSON.stringify(input) // Throws due to cycle
const { value, changes } = safeJsonValue(input)
JSON.stringify(value) // '{"one":true}"
console.log(changes) // List of changed properties
// [
// {
// path: ['self'],
// oldValue: <ref *1> { one: true, self: [Circular *1] },
// newValue: undefined,
// reason: 'unsafeCycle'
// }
// ]
Install
npm install safe-json-value
This package works in both Node.js >=14.18.0 and
browsers.
It is an ES module and must be loaded using
an import
or import()
statement,
not require()
.
API
safeJsonValue(value, options?)
value
any
\
options
Options?
\
Return value: object
Makes value
JSON-safe by:
- Omitting properties which would throw,
change type unexpectedly or
be filtered with
JSON.stringify()
- Resolving properties which would change value with
JSON.stringify()
This never throws.
Options
Object with the following properties.
maxSize
Type: number
\
Default: 1e7
Big JSON strings can make a process, filesystem operation or network request
crash. maxSize
prevents it by setting a maximum
JSON.stringify(value).length
.
Additional properties beyond the size limit are omitted. They are completely removed, not truncated (including strings).
const input = { one: true, two: 'a'.repeat(1e6) }
JSON.stringify(safeJsonValue(input, { maxSize: 1e5 }).value) // '{"one":true}"
shallow
Type: boolean
\
Default: false
If false
, object/array properties are processed recursively. Please note that
cycles are not removed when this is true
.
Return value
Object with the following properties.
value
Type: any
Copy of the input value
after applying all the changes to make
it JSON-safe.
The top-level value
itself might be changed (including to undefined
) if it
is either invalid JSON or has a toJSON()
method.
The value
is not serialized to a JSON string. This allows choosing the
serialization format (JSON, YAML, etc.), processing the value, etc.
changes
Type: Change[]
List of changes applied to value
. Each item is an
individual change to a specific property. A given property might have multiple
changes, listed in order.
changes[*].path
Type: Array<string | symbol | number>
Property path.
changes[*].oldValue
Type: any
Property value before the change.
changes[*].newValue
Type: any
Property value after the change. undefined
means the property was omitted.
changes[*].reason
Type: string
Reason for the change among:
- Exceptions:
"unsafeCycle"
,"unsafeBigInt"
,"unsafeSize"
,"unsafeException"
,"unsafeToJSON"
,"unsafeGetter"
- Invalid descriptors:
"descriptorNotWritable"
,"descriptorNotConfigurable"
- Unexpected types:
"unstableInfinite"
- Filtered values:
"ignoredFunction"
,"ignoredUndefined"
,"ignoredSymbolValue"
,"ignoredSymbolKey"
,"ignoredNotEnumerable"
,"ignoredArrayProperty"
- Unresolved values:
"unresolvedToJSON"
,"unresolvedClass"
,"unresolvedGetter"
changes[*].error
Type: Error?
Error that triggered the change. Only present if reason
is
"unsafeException"
,
"unsafeToJSON"
or
"unsafeGetter"
.
Changes
This is a list of all possible changes applied to make the value JSON-safe.
Exceptions
JSON.stringify()
can throw on specific properties. Those are omitted.
Cycles
<!-- eslint-disable fp/no-mutation -->
const input = { one: true }
input.self = input
JSON.stringify(input) // Throws due to cycle
JSON.stringify(safeJsonValue(input).value) // '{"one":true}"
Infinite recursion
const input = { toJSON: () => ({ one: true, input }) }
JSON.stringify(input) // Throws due to infinite `toJSON()` recursion
JSON.stringify(safeJsonValue(input).value) // '{"one":true,"input":{...}}"
BigInt
const input = { one: true, two: 0n }
JSON.stringify(input) // Throws due to BigInt
JSON.stringify(safeJsonValue(input).value) // '{"one":true}"
Big output
const input = { one: true, two: '\n'.repeat(5e8) }
JSON.stringify(input) // Throws due to max string length
JSON.stringify(safeJsonValue(input).value) // '{"one":true}"
Exceptions in toJSON()
const input = {
one: true,
two: {
toJSON() {
throw new Error('example')
},
},
}
JSON.stringify(input) // Throws due to `toJSON()`
JSON.stringify(safeJsonValue(input).value) // '{"one":true}"
Exceptions in getters
<!-- eslint-disable fp/no-get-set -->
const input = {
one: true,
get two() {
throw new Error('example')
},
}
JSON.stringify(input) // Throws due to `get two()`
JSON.stringify(safeJsonValue(input).value) // '{"one":true}"
Exceptions in proxies
<!-- eslint-disable fp/no-proxy -->
const input = new Proxy(
{ one: false },
{
get() {
throw new Error('example')
},
},
)
JSON.stringify(input) // Throws due to proxy
JSON.stringify(safeJsonValue(input).value) // '{}'
Invalid descriptors
Non-writable properties
<!-- eslint-disable fp/no-mutating-methods, fp/no-mutation -->
const input = {}
Object.defineProperty(input, 'one', {
value: true,
enumerable: true,
writable: false,
configurable: true,
})
input.one = false // Throws: non-writable
const safeInput = safeJsonValue(input).value
safeInput.one = false // Does not throw: now writable
Non-configurable properties
<!-- eslint-disable fp/no-mutating-methods, fp/no-mutation -->
const input = {}
Object.defineProperty(input, 'one', {
value: true,
enumerable: true,
writable: true,
configurable: false,
})
// Throws: non-configurable
Object.defineProperty(input, 'one', { value: false, enumerable: false })
const safeInput = safeJsonValue(input).value
// Does not throw: now configurable
Object.defineProperty(safeInput, 'one', { value: false, enumerable: false })
Unexpected types
JSON.stringify()
changes the types of specific values unexpectedly. Those are
omitted.
NaN and Infinity
const input = { one: true, two: Number.NaN, three: Number.POSITIVE_INFINITY }
JSON.stringify(input) // '{"one":true,"two":null,"three":null}"
JSON.stringify(safeJsonValue(input).value) // '{"one":true}"
Invalid array items
<!-- eslint-disable symbol-description -->
const input = [true, undefined, Symbol(), false]
JSON.stringify(input) // '[true, null, null, false]'
JSON.stringify(safeJsonValue(input).value) // '[true, false]'
Filtered values
JSON.stringify()
omits some specific types. Those are omitted right away to
prevent any unexpected output.
Functions
<!-- eslint-disable no-unused-expressions -->
const input = { one: true, two() {} }
JSON.parse(JSON.stringify(input)) // { one: true }
safeJsonValue(input).value // { one: true }
undefined
<!-- eslint-disable no-unused-expressions -->
const input = { one: true, two: undefined }
JSON.parse(JSON.stringify(input)) // { one: true }
safeJsonValue(input).value // { one: true }
Symbol values
<!-- eslint-disable no-unused-expressions, symbol-description -->
const input = { one: true, two: Symbol() }
JSON.parse(JSON.stringify(input)) // { one: true }
safeJsonValue(input).value // { one: true }
Symbol keys
<!-- eslint-disable no-unused-expressions, symbol-description -->
const input = { one: true, [Symbol()]: true }
JSON.parse(JSON.stringify(input)) // { one: true }
safeJsonValue(input).value // { one: true }
Non-enumerable keys
<!-- eslint-disable no-unused-expressions, fp/no-mutating-methods -->
const input = { one: true }
Object.defineProperty(input, 'two', { value: true, enumerable: false })
JSON.parse(JSON.stringify(input)) // { one: true }
safeJsonValue(input).value // { one: true }
Array properties
<!-- eslint-disable no-unused-expressions, fp/no-mutation -->
const input = [true]
input.prop = true
JSON.parse(JSON.stringify(input)) // [true]
safeJsonValue(input).value // [true]
Unresolved values
JSON.stringify()
can transform some values. Those are resolved right away to
prevent any unexpected output.
toJSON()
<!-- eslint-disable no-unused-expressions -->
const input = {
toJSON() {
return { one: true }
},
}
JSON.parse(JSON.stringify(input)) // { one: true }
safeJsonValue(input).value // { one: true }
Dates
<!-- eslint-disable no-unused-expressions -->
const input = { one: new Date() }
JSON.parse(JSON.stringify(input)) // { one: '2022-07-29T14:37:40.865Z' }
safeJsonValue(input).value // { one: '2022-07-29T14:37:40.865Z' }
Classes
<!-- eslint-disable no-unused-expressions -->
const input = { one: new Set([]) }
JSON.parse(JSON.stringify(input)) // { one: {} }
safeJsonValue(input).value // { one: {} }
Getters
<!-- eslint-disable no-unused-expressions, fp/no-get-set -->
const input = {
get one() {
return true
},
}
JSON.parse(JSON.stringify(input)) // { one: true }
safeJsonValue(input).value // { one: true }
Proxies
<!-- eslint-disable no-unused-expressions, fp/no-proxy -->
const input = new Proxy(
{ one: false },
{
get() {
return true
},
},
)
JSON.parse(JSON.stringify(input)) // { one: true }
safeJsonValue(input).value // { one: true }
Related projects
is-json-value
: Check if a value is valid JSONtruncate-json
: Truncate a JSON stringguess-json-indent
: Guess the indentation of a JSON stringerror-serializer
: Convert errors to/from plain objects
Support
For any question, don't hesitate to [submit an issue on GitHub](../../issues).
Everyone is welcome regardless of personal background. We enforce a [Code of conduct](CODE_OF_CONDUCT.md) in order to promote a positive and inclusive environment.
Contributing
This project was made with ❤️. The simplest way to give back is by starring and sharing it online.
If the documentation is unclear or has a typo, please click on the page's Edit
button (pencil icon) and suggest a correction.
If you would like to help us fix a bug or add a new feature, please check our [guidelines](CONTRIBUTING.md). Pull requests are welcome!
<!-- Thanks go to our wonderful contributors: -->
<!-- ALL-CONTRIBUTORS-LIST:START --> <!-- prettier-ignore-start --> <!-- markdownlint-disable --> ehmicky💻 🎨 🤔 📖 Pedro Augusto de Paula Barbosa📖
<!-- markdownlint-restore --> <!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->