⛑️ JSON serialization should never fail
npm install safe-json-value







⛑️ JSON serialization should never fail.
Prevent JSON.stringify() from:
- Throwing
- Changing types
- Filtering or transforming values
unexpectedly
``js
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: 1> { one: true, self: [Circular 1] },
// newValue: undefined,
// reason: 'unsafeCycle'
// }
// ]
`
`bash`
npm install safe-json-value
This package works in both Node.js >=18.18.0 and
browsers.
This is an ES module. It must be loaded using
an import or import() statement,
not require(). If TypeScript is used, it must be configured to
output ES modules,
not CommonJS.
value any\options Options?\object
_Return value_:
Makes value JSON-safe by:
- Omitting properties which would throw,
change type unexpectedly or
be filtered with JSON.stringify()JSON.stringify()
- Resolving properties which would change value with
This never throws.
Object with the following properties.
#### maxSize
_Type_: number\1e7
_Default_:
Big JSON strings can make a process, filesystem operation or network request
crash. maxSize prevents it by setting a maximumJSON.stringify(value).length.
Additional properties beyond the size limit are omitted. They are
completely removed, not truncated (including strings).
`js`
const input = { one: true, two: 'a'.repeat(1e6) }
JSON.stringify(safeJsonValue(input, { maxSize: 1e5 }).value) // '{"one":true}"
#### shallow
_Type_: boolean\false
_Default_:
If false, object/array properties are processed recursively. Please note thattrue
cycles are not removed when this is .
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 ittoJSON()
is either invalid JSON or has a 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
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".
This is a list of all possible changes applied to make the value JSON-safe.
JSON.stringify() can throw on specific properties. Those are omitted.
`js`
const input = { one: true }
input.self = input
JSON.stringify(input) // Throws due to cycle
JSON.stringify(safeJsonValue(input).value) // '{"one":true}"
`jstoJSON()
const input = { toJSON: () => ({ one: true, input }) }
JSON.stringify(input) // Throws due to infinite recursion`
JSON.stringify(safeJsonValue(input).value) // '{"one":true,"input":{...}}"
`js`
const input = { one: true, two: 0n }
JSON.stringify(input) // Throws due to BigInt
JSON.stringify(safeJsonValue(input).value) // '{"one":true}"
`js`
const input = { one: true, two: '\n'.repeat(5e8) }
JSON.stringify(input) // Throws due to max string length
JSON.stringify(safeJsonValue(input).value) // '{"one":true}"
`jstoJSON()
const input = {
one: true,
two: {
toJSON: () => {
throw new Error('example')
},
},
}
JSON.stringify(input) // Throws due to `
JSON.stringify(safeJsonValue(input).value) // '{"one":true}"
`jsget two()
const input = {
one: true,
get two() {
throw new Error('example')
},
}
JSON.stringify(input) // Throws due to `
JSON.stringify(safeJsonValue(input).value) // '{"one":true}"
`js`
const input = new Proxy(
{ one: false },
{
get: () => {
throw new Error('example')
},
},
)
JSON.stringify(input) // Throws due to proxy
JSON.stringify(safeJsonValue(input).value) // '{}'
`js`
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
`js`
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 })
JSON.stringify() changes the types of specific values unexpectedly. Those are
omitted.
`js`
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}"
`js`
const input = [true, undefined, Symbol(), false]
JSON.stringify(input) // '[true, null, null, false]'
JSON.stringify(safeJsonValue(input).value) // '[true, false]'
JSON.stringify() omits some specific types. Those are omitted right away to
prevent any unexpected output.
`js`
const input = { one: true, two: () => {} }
JSON.parse(JSON.stringify(input)) // { one: true }
safeJsonValue(input).value // { one: true }
`js`
const input = { one: true, two: undefined }
JSON.parse(JSON.stringify(input)) // { one: true }
safeJsonValue(input).value // { one: true }
`js`
const input = { one: true, two: Symbol() }
JSON.parse(JSON.stringify(input)) // { one: true }
safeJsonValue(input).value // { one: true }
`js`
const input = { one: true, [Symbol()]: true }
JSON.parse(JSON.stringify(input)) // { one: true }
safeJsonValue(input).value // { one: true }
`js`
const input = { one: true }
Object.defineProperty(input, 'two', { value: true, enumerable: false })
JSON.parse(JSON.stringify(input)) // { one: true }
safeJsonValue(input).value // { one: true }
`js`
const input = [true]
input.prop = true
JSON.parse(JSON.stringify(input)) // [true]
safeJsonValue(input).value // [true]
JSON.stringify() can transform some values. Those are resolved right away to
prevent any unexpected output.
`js`
const input = {
toJSON: () => ({ one: true }),
}
JSON.parse(JSON.stringify(input)) // { one: true }
safeJsonValue(input).value // { one: true }
`js`
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' }
`js`
const input = { one: new Set([]) }
JSON.parse(JSON.stringify(input)) // { one: {} }
safeJsonValue(input).value // { one: {} }
`js`
const input = {
get one() {
return true
},
}
JSON.parse(JSON.stringify(input)) // { one: true }
safeJsonValue(input).value // { one: true }
`js`
const input = new Proxy(
{ one: false },
{
get: () => true,
},
)
JSON.parse(JSON.stringify(input)) // { one: true }
safeJsonValue(input).value // { one: true }
- is-json-value: Check if a value
is valid JSON
- truncate-json: Truncate a JSON
string
- guess-json-indent: Guess the
indentation of a JSON string
- error-serializer: Convert
errors to/from plain objects
For any question, _don't hesitate_ to submit an issue on GitHub.
Everyone is welcome regardless of personal background. We enforce a
Code of conduct in order to promote a positive and
inclusive environment.
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. Pull requests are welcome!
ehmicky 💻 🎨 🤔 📖 | Pedro Augusto de Paula Barbosa 📖 |