The JSON.stringify()
function is the canonical way to convert a JavaScript object to JSON. Many JavaScript frameworks use JSON.stringify()
under the hood: Express’ res.json()
, Axios’ post()
, and Webpack stats all call JSON.stringify()
. In this article, I’ll provide a practical overview of JSON.stringify()
, including error cases.
Getting Started
All modern JavaScript runtimes support JSON.stringify()
. Even Internet Explorer has JSON.stringify()
support since IE8. Here’s an example of converting a simple object to JSON:
const obj = { answer: 42 };
const str = JSON.stringify(obj);
str; // '{"answer":42}'
typeof str; // 'string'
You may see JSON.stringify()
used with JSON.parse()
as shown below. This pattern is one way of deep cloning a JavaScript object.
const obj = { answer: 42 };
const clone = JSON.parse(JSON.stringify(obj));
clone.answer; // 42
clone === obj; // false
Errors and Edge Cases
JSON.stringify()
throws an error when it detects a cyclical object. In other words, if an object obj
has a property whose value is obj
, JSON.stringify()
will throw an error.
const obj = {};
// Cyclical object that references itself
obj.prop = obj;
// Throws "TypeError: TypeError: Converting circular structure to JSON"
JSON.stringify(obj);
That is the only case where JSON.stringify()
throws an exception, unless you’re using custom toJSON()
functions or replacer functions. However, you should still wrap JSON.stringify()
calls in try/catch
, because circular objects do pop up in practice.
There are several edge cases where JSON.stringify()
doesn’t throw an error, but you might expect it does. For example, JSON.stringify()
converts NaN
and Infinity
to null
:
const obj = { nan: parseInt('not a number'), inf: Number.POSITIVE_INFINITY };
JSON.stringify(obj); // '{"nan":null,"inf":null}'
JSON.stringify()
also strips out properties whose values are functions or undefined
:
const obj = { fn: function() {}, undef: undefined };
// Empty object. `JSON.stringify()` removes functions and `undefined`.
JSON.stringify(obj); // '{}'
Pretty Printing
The first parameter to JSON.stringify()
is the object to serialize to JSON. JSON.stringify()
actually takes 3 parameters, and the 3rd parameter is called spaces
. The spaces
parameter is used for formatting JSON output in a human readable way.
You can set the spaces
parameter to either be a string or a number. If spaces
is not undefined, JSON.stringify()
will put each key in the JSON output on its own line, and prefix each key with spaces
.
const obj = { a: 1, b: 2, c: 3, d: { e: 4 } };
// '{"a":1,"b":2,"c":3,"d":{"e":4}}'
JSON.stringify(obj);
// {
// "a": 1,
// "b": 2,
// "c": 3,
// "d": {
// "e": 4
// }
// }
JSON.stringify(obj, null, ' ');
// Use 2 spaces when formatting JSON output. Equivalent to the above.
JSON.stringify(obj, null, 2);
The spaces
string doesn’t have to be all whitespace, although in practice it usually will be. For example:
// {
// __"a": 1,
// __"b": 2,
// __"c": 3,
// __"d": {
// ____"e": 4
// __}
// }
JSON.stringify(obj, null, '__');
Replacers
The 2nd parameter to JSON.stringify()
is the replacer
function. In the above examples, replacer
was null
. JavaScript calls the replacer function with every key/value pair in the object, and uses the value the replacer function returns. For example:
const obj = { a: 1, b: 2, c: 3, d: { e: 4 } };
// `replacer` increments every number value by 1. The output will be:
// '{"a":2,"b":3,"c":4,"d":{"e":5}}'
JSON.stringify(obj, function replacer(key, value) {
if (typeof value === 'number') {
return value + 1;
}
return value;
});
The replacer function is useful for omitting sensitive data. For example, suppose you wanted to omit all keys that contain the substring ‘password’:
const obj = {
name: 'Jean-Luc Picard',
password: 'stargazer',
nested: {
hashedPassword: 'c3RhcmdhemVy'
}
};
// '{"name":"Jean-Luc Picard","nested":{}}'
JSON.stringify(obj, function replacer(key, value) {
// This function gets called 5 times. `key` will be equal to:
// '', 'name', 'password', 'nested', 'hashedPassword'
if (key.match(/password/i)) {
return undefined;
}
return value;
});
The toJSON()
Function
The JSON.stringify()
function also traverses the object looking for properties that have a toJSON()
function. If it finds a toJSON()
function, JSON.stringify()
calls the toJSON()
function and uses the return value as a replacement. For example:
const obj = {
name: 'Jean-Luc Picard',
nested: {
test: 'not in output',
toJSON: () => 'test'
}
};
// '{"name":"Jean-Luc Picard","nested":"test"}'
JSON.stringify(obj);
The toJSON()
function can return any value, including objects, primitives, or undefined
. If toJSON()
returns undefined
, JSON.stringify()
will ignore that property.
Many JavaScript modules use toJSON()
to ensure sophisticated objects get serialized correctly, like Mongoose documents and Moment objects.
Moving On
The JSON.stringify()
function is a core JavaScript fundamental. Many libraries and frameworks use it under the hood, so a solid understanding of JSON.stringify()
lets you do more with your favorite npm modules. For example, you can define alternate date formatting for your Express REST API using a custom toJSON()
function on the native Date
class, or ensure that a circular client-side object gets converted to JSON correctly when sending an HTTP request with Axios.
Found a typo or error? Open up a pull request! This post is
available as markdown on Github
Содержание
- papb/jsonify-error
- Sign In Required
- Launching GitHub Desktop
- Launching GitHub Desktop
- Launching Xcode
- Launching Visual Studio Code
- Latest commit
- Git stats
- Files
- README.md
- stdlib-js/error-to-json
- Sign In Required
- Launching GitHub Desktop
- Launching GitHub Desktop
- Launching Xcode
- Launching Visual Studio Code
- Latest commit
- Git stats
- Files
- README.md
- Формат JSON, метод toJSON
- JSON.stringify
- Исключаем и преобразуем: replacer
- Форматирование: space
- Пользовательский «toJSON»
- JSON.stringify()
- Try it
- Syntax
- Parameters
- Return value
- Exceptions
- Description
- The replacer parameter
- The space parameter
- Examples
- Using JSON.stringify
- Using a function as replacer
- Using an array as replacer
- Using the space parameter
- toJSON() behavior
- Issue with serializing circular references
papb/jsonify-error
Use Git or checkout with SVN using the web URL.
Work fast with our official CLI. Learn more.
Sign In Required
Please sign in to use Codespaces.
Launching GitHub Desktop
If nothing happens, download GitHub Desktop and try again.
Launching GitHub Desktop
If nothing happens, download GitHub Desktop and try again.
Launching Xcode
If nothing happens, download Xcode and try again.
Launching Visual Studio Code
Your codespace will open once ready.
There was a problem preparing your codespace, please try again.
Latest commit
Git stats
Files
Failed to load latest commit information.
README.md
Convert errors to JSON or to a good string. Develop faster with better error messages.
It’s 2019 and still the default behavior of JavaScript could be better with regard to displaying/manipulating errors:
- JSON.stringify(e) : Bad
- e.toString() : Bad
- e.toJSON() : Doesn’t exist
- console.log(e) : Bad in browsers, not so bad in Node but could be better
But jsonify-error comes to the rescue:
- For JSON.stringify(e) :
- Use JSON.stringify(jsonifyError(e)) instead
- Or call jsonifyError.overrideErrorMethods() once and then JSON.stringify(e) will work.
- For e.toString() :
- Use jsonifyError.asString(e) instead
- Or call jsonifyError.overrideErrorMethods() once and then e.toString() will work.
- For e.toJSON() :
- Use jsonifyError(e) instead
- Or call jsonifyError.overrideErrorMethods() once and then e.toJSON() will work.
- For console.log(e) :
- Use jsonifyError.log(e) instead
- Or call jsonifyError.overrideConsole() once and then console.log(e) will work.
For browsers, simply include one of the dists in your entry point, such as dist/jsonify-error.js . The dists are available in jsDelivr:
They are also available as GitHub release assets (since 1.4.2). The following formats are available (with source maps):
- jsonify-error.js
- jsonify-error.min.js (minified)
- jsonify-error.es5.js (ES5 compatible)
- jsonify-error.es5.min.js (ES5 compatible, minified)
Or if you’re developing a browser library with Browserify, you can just require it normally, as if you were in a Node environment.
In node, as usual, simply do:
The main purpose of jsonify-error, as the name suggests, is to convert an error to a plain object. Just do jsonifyError(e) and you will get something like:
If you’re thinking «Great! Now I can do console.log(jsonifyError(e)) instead of console.log(e) » in a browser, you’re in the right track, but you can do even better! A few utility methods are exposed by jsonifyError beyond the main one, as mentioned in the beginning of this README.
- jsonifyError.log(e) : Logs the error in a much better way than console.log(e) .
- jsonifyError.overrideConsole() : Makes console.log , console.warn , console.error work like jsonifyError.log automatically. Calling this once is enough.
- jsonifyError.overrideErrorMethods() : Heavily improves e.toString() and adds e.toJSON() to all errors automatically. Calling this once is enough.
Example: with try-catch blocks
Example: with promises
Also, for promises, there is a sibling module called better-promise-error-log which takes care of showing the improved logs automatically for unhandled rejections.
Example: with express
Note: if you’ve overriden error methods (by calling jsonifyError.overrideErrorMethods() ), the above can be simplified to res.status(500).json(error) (see the overriding methods section).
Example usage: overriding methods
Any contribution is very welcome. Feel free to open an issue about anything: questions, suggestions, feature requests, bugs, improvements, mistakes, whatever. I will be always looking.
Источник
stdlib-js/error-to-json
Use Git or checkout with SVN using the web URL.
Work fast with our official CLI. Learn more.
Sign In Required
Please sign in to use Codespaces.
Launching GitHub Desktop
If nothing happens, download GitHub Desktop and try again.
Launching GitHub Desktop
If nothing happens, download GitHub Desktop and try again.
Launching Xcode
If nothing happens, download Xcode and try again.
Launching Visual Studio Code
Your codespace will open once ready.
There was a problem preparing your codespace, please try again.
Latest commit
Git stats
Files
Failed to load latest commit information.
README.md
Return a JSON representation of an error object.
- To load the package in a website via a script tag without installation and bundlers, use the ES Module available on the esm branch.
- If you are using Deno, visit the deno branch.
- For use in Observable, or in browser/node environments, use the Universal Module Definition (UMD) build available on the umd branch.
The branches.md file summarizes the available branches and displays a diagram illustrating their relationships.
Returns a JSON representation of an error object.
The JSON object is guaranteed to have the following properties:
- type: error type.
- message: error message.
The only standardized cross-platform property is message . Depending on the platform, the following properties may be present:
- name: error name.
- stack: stack trace.
- code: error code (Node.js system errors).
- errno: error code string (Node.js system errors).
- syscall: string representing the failed system call (Node.js system errors).
The function also serializes all enumerable properties.
Supported built-in error types:
The implementation supports custom error types and sets the type field to the closest built-in error type.
- @stdlib/error/reviver : revive a JSON-serialized error object.
This package is part of stdlib, a standard library for JavaScript and Node.js, with an emphasis on numerical and scientific computing. The library provides a collection of robust, high performance libraries for mathematics, statistics, streams, utilities, and more.
For more information on the project, filing bug reports and feature requests, and guidance on how to develop stdlib, see the main project repository.
Источник
Формат JSON, метод toJSON
Допустим, у нас есть сложный объект, и мы хотели бы преобразовать его в строку, чтобы отправить по сети или просто вывести для логирования.
Естественно, такая строка должна включать в себя все важные свойства.
Мы могли бы реализовать преобразование следующим образом:
…Но в процессе разработки добавляются новые свойства, старые свойства переименовываются и удаляются. Обновление такого toString каждый раз может стать проблемой. Мы могли бы попытаться перебрать свойства в нём, но что, если объект сложный, и в его свойствах имеются вложенные объекты? Мы должны были бы осуществить их преобразование тоже.
К счастью, нет необходимости писать код для обработки всего этого. У задачи есть простое решение.
JSON.stringify
JSON (JavaScript Object Notation) – это общий формат для представления значений и объектов. Его описание задокументировано в стандарте RFC 4627. Первоначально он был создан для JavaScript, но многие другие языки также имеют библиотеки, которые могут работать с ним. Таким образом, JSON легко использовать для обмена данными, когда клиент использует JavaScript, а сервер написан на Ruby/PHP/Java или любом другом языке.
JavaScript предоставляет методы:
- JSON.stringify для преобразования объектов в JSON.
- JSON.parse для преобразования JSON обратно в объект.
Например, здесь мы преобразуем через JSON.stringify данные студента:
Метод JSON.stringify(student) берёт объект и преобразует его в строку.
Полученная строка json называется JSON-форматированным или сериализованным объектом. Мы можем отправить его по сети или поместить в обычное хранилище данных.
Обратите внимание, что объект в формате JSON имеет несколько важных отличий от объектного литерала:
- Строки используют двойные кавычки. Никаких одинарных кавычек или обратных кавычек в JSON. Так ‘John’ становится «John» .
- Имена свойств объекта также заключаются в двойные кавычки. Это обязательно. Так age:30 становится «age»:30 .
JSON.stringify может быть применён и к примитивам.
JSON поддерживает следующие типы данных:
- Объекты
- Массивы [ . ]
- Примитивы:
- строки,
- числа,
- логические значения true/false ,
- null .
JSON является независимой от языка спецификацией для данных, поэтому JSON.stringify пропускает некоторые специфические свойства объектов JavaScript.
- Свойства-функции (методы).
- Символьные ключи и значения.
- Свойства, содержащие undefined .
Обычно это нормально. Если это не то, чего мы хотим, то скоро мы увидим, как можно настроить этот процесс.
Самое замечательное, что вложенные объекты поддерживаются и конвертируются автоматически.
Важное ограничение: не должно быть циклических ссылок.
Здесь преобразование завершается неудачно из-за циклической ссылки: room.occupiedBy ссылается на meetup , и meetup.place ссылается на room :
Исключаем и преобразуем: replacer
Полный синтаксис JSON.stringify :
В большинстве случаев JSON.stringify используется только с первым аргументом. Но если нам нужно настроить процесс замены, например, отфильтровать циклические ссылки, то можно использовать второй аргумент JSON.stringify .
Если мы передадим ему массив свойств, будут закодированы только эти свойства.
Здесь мы, наверное, слишком строги. Список свойств применяется ко всей структуре объекта. Так что внутри participants – пустые объекты, потому что name нет в списке.
Давайте включим в список все свойства, кроме room.occupiedBy , из-за которого появляется цикличная ссылка:
Теперь всё, кроме occupiedBy , сериализовано. Но список свойств довольно длинный.
К счастью, в качестве replacer мы можем использовать функцию, а не массив.
Функция будет вызываться для каждой пары (key, value) , и она должна возвращать заменённое значение, которое будет использоваться вместо исходного. Или undefined , чтобы пропустить значение.
В нашем случае мы можем вернуть value «как есть» для всего, кроме occupiedBy . Чтобы игнорировать occupiedBy , код ниже возвращает undefined :
Обратите внимание, что функция replacer получает каждую пару ключ/значение, включая вложенные объекты и элементы массива. И она применяется рекурсивно. Значение this внутри replacer – это объект, который содержит текущее свойство.
Первый вызов – особенный. Ему передаётся специальный «объект-обёртка»: <«»: meetup>. Другими словами, первая (key, value) пара имеет пустой ключ, а значением является целевой объект в общем. Вот почему первая строка из примера выше будет «:[object Object]» .
Идея состоит в том, чтобы дать как можно больше возможностей replacer – у него есть возможность проанализировать и заменить/пропустить даже весь объект целиком, если это необходимо.
Форматирование: space
Третий аргумент в JSON.stringify(value, replacer, space) – это количество пробелов, используемых для удобного форматирования.
Ранее все JSON-форматированные объекты не имели отступов и лишних пробелов. Это нормально, если мы хотим отправить объект по сети. Аргумент space используется исключительно для вывода в удобочитаемом виде.
Ниже space = 2 указывает JavaScript отображать вложенные объекты в несколько строк с отступом в 2 пробела внутри объекта:
Третьим аргументом также может быть строка. В этом случае строка будет использоваться для отступа вместо ряда пробелов.
Параметр space применяется исключительно для логирования и красивого вывода.
Пользовательский «toJSON»
Как и toString для преобразования строк, объект может предоставлять метод toJSON для преобразования в JSON. JSON.stringify автоматически вызывает его, если он есть.
Как видим, date (1) стал строкой. Это потому, что все объекты типа Date имеют встроенный метод toJSON , который возвращает такую строку.
Теперь давайте добавим собственную реализацию метода toJSON в наш объект room (2) :
Источник
JSON.stringify()
The JSON.stringify() static method converts a JavaScript value to a JSON string, optionally replacing values if a replacer function is specified or optionally including only the specified properties if a replacer array is specified.
Try it
Syntax
Parameters
The value to convert to a JSON string.
A function that alters the behavior of the stringification process, or an array of strings and numbers that specifies properties of value to be included in the output. If replacer is an array, all elements in this array that are not strings or numbers (either primitives or wrapper objects), including Symbol values, are completely ignored. If replacer is anything other than a function or an array (e.g. null or not provided), all string-keyed properties of the object are included in the resulting JSON string.
A string or number that’s used to insert white space (including indentation, line break characters, etc.) into the output JSON string for readability purposes.
If this is a number, it indicates the number of space characters to be used as indentation, clamped to 10 (that is, any number greater than 10 is treated as if it were 10 ). Values less than 1 indicate that no space should be used.
If this is a string, the string (or the first 10 characters of the string, if it’s longer than that) is inserted before every nested object or array.
If space is anything other than a string or number (can be either a primitive or a wrapper object) — for example, is null or not provided — no white space is used.
Return value
A JSON string representing the given value, or undefined.
Exceptions
Thrown if one of the following is true:
- value contains a circular reference.
- A BigInt value is encountered.
Description
JSON.stringify() converts a value to the JSON notation that the value represents. Values are stringified in the following manner:
- Boolean , Number , String , and BigInt (obtainable via Object() ) objects are converted to the corresponding primitive values during stringification, in accordance with the traditional conversion semantics. Symbol objects (obtainable via Object() ) are treated as plain objects.
- Attempting to serialize BigInt values will throw. However, if the BigInt has a toJSON() method (through monkey patching: BigInt.prototype.toJSON = . ), that method can provide the serialization result. This constraint ensures that a proper serialization (and, very likely, its accompanying deserialization) behavior is always explicitly provided by the user.
- undefined , Function , and Symbol values are not valid JSON values. If any such values are encountered during conversion, they are either omitted (when found in an object) or changed to null (when found in an array). JSON.stringify() can return undefined when passing in «pure» values like JSON.stringify(() =><>) or JSON.stringify(undefined) .
- The numbers Infinity and NaN , as well as the value null , are all considered null . (But unlike the values in the previous point, they would never be omitted.)
- Arrays are serialized as arrays (enclosed by square brackets). Only array indices between 0 and length — 1 (inclusive) are serialized; other properties are ignored.
- For other objects:
- All Symbol -keyed properties will be completely ignored, even when using the replacer parameter.
- If the value has a toJSON() method, it’s responsible to define what data will be serialized. Instead of the object being serialized, the value returned by the toJSON() method when called will be serialized. JSON.stringify() calls toJSON with one parameter, the key , which has the same semantic as the key parameter of the replacer function:
- if this object is a property value, the property name
- if it is in an array, the index in the array, as a string
- if JSON.stringify() was directly called on this object, an empty string
Date objects implement the toJSON() method which returns a string (the same as date.toISOString() ). Thus, they will be stringified as strings.
- Only enumerable own properties are visited. This means Map , Set , etc. will become «<>» . You can use the replacer parameter to serialize them to something more useful. Properties are visited using the same algorithm as Object.keys() , which has a well-defined order and is stable across implementations. For example, JSON.stringify on the same object will always produce the same string, and JSON.parse(JSON.stringify(obj)) would produce an object with the same key ordering as the original (assuming the object is completely JSON-serializable).
The replacer parameter
The replacer parameter can be either a function or an array.
As an array, its elements indicate the names of the properties in the object that should be included in the resulting JSON string. Only string and number values are taken into account; symbol keys are ignored.
As a function, it takes two parameters: the key and the value being stringified. The object in which the key was found is provided as the replacer ‘s this context.
The replacer function is called for the initial object being stringified as well, in which case the key is an empty string ( «» ). It is then called for each property on the object or array being stringified. Array indices will be provided in its string form as key . The current property value will be replaced with the replacer ‘s return value for stringification. This means:
- If you return a number, string, boolean, or null , that value is directly serialized and used as the property’s value. (Returning a BigInt will throw as well.)
- If you return a Function , Symbol , or undefined , the property is not included in the output.
- If you return any other object, the object is recursively stringified, calling the replacer function on each property.
Note: When parsing JSON generated with replacer functions, you would likely want to use the reviver parameter to perform the reverse operation.
Typically, array elements’ index would never shift (even when the element is an invalid value like a function, it will become null instead of omitted). Using the replacer function allows you to control the order of the array elements by returning a different array.
The space parameter
The space parameter may be used to control spacing in the final string.
- If it is a number, successive levels in the stringification will each be indented by this many space characters.
- If it is a string, successive levels will be indented by this string.
Each level of indentation will never be longer than 10. Number values of space are clamped to 10, and string values are truncated to 10 characters.
Examples
Using JSON.stringify
Using a function as replacer
If you wish the replacer to distinguish an initial object from a key with an empty string property (since both would give the empty string as key and potentially an object as value), you will have to keep track of the iteration count (if it is beyond the first iteration, it is a genuine empty string key).
Using an array as replacer
Using the space parameter
Indent the output with one space:
Using a tab character mimics standard pretty-print appearance:
toJSON() behavior
Defining toJSON() for an object allows overriding its serialization behavior.
Issue with serializing circular references
Since the JSON format doesn’t support object references (although an IETF draft exists), a TypeError will be thrown if one attempts to encode an object with circular references.
Источник
The JSON.stringify()
static method converts a JavaScript value to a JSON string, optionally replacing values if a replacer function is specified or optionally including only the specified properties if a replacer array is specified.
Try it
Syntax
JSON.stringify(value)
JSON.stringify(value, replacer)
JSON.stringify(value, replacer, space)
Parameters
value
-
The value to convert to a JSON string.
replacer
Optional-
A function that alters the behavior of the stringification process, or an array of strings and numbers that specifies properties of
value
to be included in the output. Ifreplacer
is an array, all elements in this array that are not strings or numbers (either primitives or wrapper objects), includingSymbol
values, are completely ignored. Ifreplacer
is anything other than a function or an array (e.g.null
or not provided), all string-keyed properties of the object are included in the resulting JSON string. space
Optional-
A string or number that’s used to insert white space (including indentation, line break characters, etc.) into the output JSON string for readability purposes.
If this is a number, it indicates the number of space characters to be used as indentation, clamped to 10 (that is, any number greater than
10
is treated as if it were10
). Values less than 1 indicate that no space should be used.If this is a string, the string (or the first 10 characters of the string, if it’s longer than that) is inserted before every nested object or array.
If
space
is anything other than a string or number (can be either a primitive or a wrapper object) — for example, isnull
or not provided — no white space is used.
Return value
A JSON string representing the given value, or undefined.
Exceptions
TypeError
-
Thrown if one of the following is true:
value
contains a circular reference.- A
BigInt
value is encountered.
Description
JSON.stringify()
converts a value to the JSON notation that the value represents. Values are stringified in the following manner:
Boolean
,Number
,String
, andBigInt
(obtainable viaObject()
) objects are converted to the corresponding primitive values during stringification, in accordance with the traditional conversion semantics.Symbol
objects (obtainable viaObject()
) are treated as plain objects.- Attempting to serialize
BigInt
values will throw. However, if the BigInt has atoJSON()
method (through monkey patching:BigInt.prototype.toJSON = ...
), that method can provide the serialization result. This constraint ensures that a proper serialization (and, very likely, its accompanying deserialization) behavior is always explicitly provided by the user. undefined
,Function
, andSymbol
values are not valid JSON values. If any such values are encountered during conversion, they are either omitted (when found in an object) or changed tonull
(when found in an array).JSON.stringify()
can returnundefined
when passing in «pure» values likeJSON.stringify(() => {})
orJSON.stringify(undefined)
.- The numbers
Infinity
andNaN
, as well as the valuenull
, are all considerednull
. (But unlike the values in the previous point, they would never be omitted.) - Arrays are serialized as arrays (enclosed by square brackets). Only array indices between 0 and
length - 1
(inclusive) are serialized; other properties are ignored. - For other objects:
- All
Symbol
-keyed properties will be completely ignored, even when using thereplacer
parameter. - If the value has a
toJSON()
method, it’s responsible to define what data will be serialized. Instead of the object being serialized, the value returned by thetoJSON()
method when called will be serialized.JSON.stringify()
callstoJSON
with one parameter, thekey
, which has the same semantic as thekey
parameter of thereplacer
function:- if this object is a property value, the property name
- if it is in an array, the index in the array, as a string
- if
JSON.stringify()
was directly called on this object, an empty string
Date
objects implement thetoJSON()
method which returns a string (the same asdate.toISOString()
). Thus, they will be stringified as strings. -
Only enumerable own properties are visited. This means
Map
,Set
, etc. will become"{}"
. You can use thereplacer
parameter to serialize them to something more useful.
Properties are visited using the same algorithm asObject.keys()
, which has a well-defined order and is stable across implementations. For example,JSON.stringify
on the same object will always produce the same string, andJSON.parse(JSON.stringify(obj))
would produce an object with the same key ordering as the original (assuming the object is completely JSON-serializable).
- All
The replacer parameter
The replacer
parameter can be either a function or an array.
As an array, its elements indicate the names of the properties in the object that should be included in the resulting JSON string. Only string and number values are taken into account; symbol keys are ignored.
As a function, it takes two parameters: the key
and the value
being stringified. The object in which the key was found is provided as the replacer
‘s this
context.
The replacer
function is called for the initial object being stringified as well, in which case the key
is an empty string (""
). It is then called for each property on the object or array being stringified. Array indices will be provided in its string form as key
. The current property value will be replaced with the replacer
‘s return value for stringification. This means:
- If you return a number, string, boolean, or
null
, that value is directly serialized and used as the property’s value. (Returning a BigInt will throw as well.) - If you return a
Function
,Symbol
, orundefined
, the property is not included in the output. - If you return any other object, the object is recursively stringified, calling the
replacer
function on each property.
Note: When parsing JSON generated with replacer
functions, you would likely want to use the reviver
parameter to perform the reverse operation.
Typically, array elements’ index would never shift (even when the element is an invalid value like a function, it will become null
instead of omitted). Using the replacer
function allows you to control the order of the array elements by returning a different array.
The space parameter
The space
parameter may be used to control spacing in the final string.
- If it is a number, successive levels in the stringification will each be indented by this many space characters.
- If it is a string, successive levels will be indented by this string.
Each level of indentation will never be longer than 10. Number values of space
are clamped to 10, and string values are truncated to 10 characters.
Examples
Using JSON.stringify
JSON.stringify({}); // '{}'
JSON.stringify(true); // 'true'
JSON.stringify("foo"); // '"foo"'
JSON.stringify([1, "false", false]); // '[1,"false",false]'
JSON.stringify([NaN, null, Infinity]); // '[null,null,null]'
JSON.stringify({ x: 5 }); // '{"x":5}'
JSON.stringify(new Date(1906, 0, 2, 15, 4, 5));
// '"1906-01-02T15:04:05.000Z"'
JSON.stringify({ x: 5, y: 6 });
// '{"x":5,"y":6}'
JSON.stringify([new Number(3), new String("false"), new Boolean(false)]);
// '[3,"false",false]'
// String-keyed array elements are not enumerable and make no sense in JSON
const a = ["foo", "bar"];
a["baz"] = "quux"; // a: [ 0: 'foo', 1: 'bar', baz: 'quux' ]
JSON.stringify(a);
// '["foo","bar"]'
JSON.stringify({ x: [10, undefined, function () {}, Symbol("")] });
// '{"x":[10,null,null,null]}'
// Standard data structures
JSON.stringify([
new Set([1]),
new Map([[1, 2]]),
new WeakSet([{ a: 1 }]),
new WeakMap([[{ a: 1 }, 2]]),
]);
// '[{},{},{},{}]'
// TypedArray
JSON.stringify([new Int8Array([1]), new Int16Array([1]), new Int32Array([1])]);
// '[{"0":1},{"0":1},{"0":1}]'
JSON.stringify([
new Uint8Array([1]),
new Uint8ClampedArray([1]),
new Uint16Array([1]),
new Uint32Array([1]),
]);
// '[{"0":1},{"0":1},{"0":1},{"0":1}]'
JSON.stringify([new Float32Array([1]), new Float64Array([1])]);
// '[{"0":1},{"0":1}]'
// toJSON()
JSON.stringify({
x: 5,
y: 6,
toJSON() {
return this.x + this.y;
},
});
// '11'
// Symbols:
JSON.stringify({ x: undefined, y: Object, z: Symbol("") });
// '{}'
JSON.stringify({ [Symbol("foo")]: "foo" });
// '{}'
JSON.stringify({ [Symbol.for("foo")]: "foo" }, [Symbol.for("foo")]);
// '{}'
JSON.stringify({ [Symbol.for("foo")]: "foo" }, (k, v) => {
if (typeof k === "symbol") {
return "a symbol";
}
});
// undefined
// Non-enumerable properties:
JSON.stringify(
Object.create(null, {
x: { value: "x", enumerable: false },
y: { value: "y", enumerable: true },
}),
);
// '{"y":"y"}'
// BigInt values throw
JSON.stringify({ x: 2n });
// TypeError: BigInt value can't be serialized in JSON
Using a function as replacer
function replacer(key, value) {
// Filtering out properties
if (typeof value === "string") {
return undefined;
}
return value;
}
const foo = {
foundation: "Mozilla",
model: "box",
week: 45,
transport: "car",
month: 7,
};
JSON.stringify(foo, replacer);
// '{"week":45,"month":7}'
If you wish the replacer
to distinguish an initial object from a key with an empty string property (since both would give the empty string as key and potentially an object as value), you will have to keep track of the iteration count (if it is beyond the first iteration, it is a genuine empty string key).
function makeReplacer() {
let isInitial = true;
return (key, value) => {
if (isInitial) {
isInitial = false;
return value;
}
if (key === "") {
// Omit all properties with name "" (except the initial object)
return undefined;
}
return value;
};
}
const replacer = makeReplacer();
console.log(JSON.stringify({ "": 1, b: 2 }, replacer)); // "{"b":2}"
Using an array as replacer
const foo = {
foundation: "Mozilla",
model: "box",
week: 45,
transport: "car",
month: 7,
};
JSON.stringify(foo, ["week", "month"]);
// '{"week":45,"month":7}', only keep "week" and "month" properties
Using the space parameter
Indent the output with one space:
console.log(JSON.stringify({ a: 2 }, null, " "));
/*
{
"a": 2
}
*/
Using a tab character mimics standard pretty-print appearance:
console.log(JSON.stringify({ uno: 1, dos: 2 }, null, "t"));
/*
{
"uno": 1,
"dos": 2
}
*/
toJSON() behavior
Defining toJSON()
for an object allows overriding its serialization behavior.
const obj = {
data: "data",
toJSON(key) {
return key ? `Now I am a nested object under key '${key}'` : this;
},
};
JSON.stringify(obj);
// '{"data":"data"}'
JSON.stringify({ obj });
// '{"obj":"Now I am a nested object under key 'obj'"}'
JSON.stringify([obj]);
// '["Now I am a nested object under key '0'"]'
Issue with serializing circular references
Since the JSON format doesn’t support object references (although an IETF draft exists), a TypeError
will be thrown if one attempts to encode an object with circular references.
const circularReference = {};
circularReference.myself = circularReference;
// Serializing circular references throws "TypeError: cyclic object value"
JSON.stringify(circularReference);
To serialize circular references, you can use a library that supports them (e.g. cycle.js by Douglas Crockford) or implement a solution yourself, which will require finding and replacing (or removing) the cyclic references by serializable values.
If you are using JSON.stringify()
to deep-copy an object, you may instead want to use structuredClone()
, which supports circular references. JavaScript engine APIs for binary serialization, such as v8.serialize()
, also support circular references.
Using JSON.stringify() with localStorage
In a case where you want to store an object created by your user and allow it to be restored even after the browser has been closed, the following example is a model for the applicability of JSON.stringify()
:
// Creating an example of JSON
const session = {
screens: [],
state: true,
};
session.screens.push({ name: "screenA", width: 450, height: 250 });
session.screens.push({ name: "screenB", width: 650, height: 350 });
session.screens.push({ name: "screenC", width: 750, height: 120 });
session.screens.push({ name: "screenD", width: 250, height: 60 });
session.screens.push({ name: "screenE", width: 390, height: 120 });
session.screens.push({ name: "screenF", width: 1240, height: 650 });
// Converting the JSON string with JSON.stringify()
// then saving with localStorage in the name of session
localStorage.setItem("session", JSON.stringify(session));
// Example of how to transform the String generated through
// JSON.stringify() and saved in localStorage in JSON object again
const restoredSession = JSON.parse(localStorage.getItem("session"));
// Now restoredSession variable contains the object that was saved
// in localStorage
console.log(restoredSession);
Well-formed JSON.stringify()
Engines implementing the well-formed JSON.stringify specification will stringify lone surrogates (any code point from U+D800 to U+DFFF) using Unicode escape sequences rather than literally (outputting lone surrogates). Before this change, such strings could not be encoded in valid UTF-8 or UTF-16:
JSON.stringify("uD800"); // '"�"'
But with this change JSON.stringify()
represents lone surrogates using JSON escape sequences that can be encoded in valid UTF-8 or UTF-16:
JSON.stringify("uD800"); // '"\ud800"'
This change should be backwards-compatible as long as you pass the result of JSON.stringify()
to APIs such as JSON.parse()
that will accept any valid JSON text, because they will treat Unicode escapes of lone surrogates as identical to the lone surrogates themselves. Only if you are directly interpreting the result of JSON.stringify()
do you need to carefully handle JSON.stringify()
‘s two possible encodings of these code points.
Specifications
Specification |
---|
ECMAScript Language Specification # sec-json.stringify |
Browser compatibility
BCD tables only load in the browser
See also
Время прочтения
5 мин
Просмотры 389K
JavaScript может быть кошмаром при отладке: некоторые ошибки, которые он выдает, могут быть очень трудны для понимания с первого взгляда, и выдаваемые номера строк также не всегда полезны. Разве не было бы полезно иметь список, глядя на который, можно понять смысл ошибок и как исправить их? Вот он!
Ниже представлен список странных ошибок в JavaScript. Разные браузеры могут выдавать разные сообщения об одинаковых ошибках, поэтому приведено несколько примеров там, где возможно.
Как читать ошибки?
Перед самим списком, давайте быстро взглянем на структуру сообщения об ошибке. Понимание структуры помогает понимать ошибки, и вы получите меньше проблем, если наткнетесь на ошибки, не представленные в этом списке.
Типичная ошибка из Chrome выглядит так:
Uncaught TypeError: undefined is not a function
Структура ошибки следующая:
- Uncaught TypeError: эта часть сообщения обычно не особо полезна.
Uncaught
значит, что ошибка не была перехвачена вcatch
, аTypeError
— это название ошибки. - undefined is not a function: это та самая часть про ошибку. В случае с сообщениями об ошибках, читать их нужно прямо буквально. Например, в этом случае, она значит то, что код попытался использовать значение
undefined
как функцию.
Другие webkit-браузеры, такие как Safari, выдают ошибки примерно в таком же формате, как и Chrome. Ошибки из Firefox похожи, но не всегда включают в себя первую часть, и последние версии Internet Explorer также выдают более простые ошибки, но в этом случае проще — не всегда значит лучше.
Теперь к самим ошибкам.
Uncaught TypeError: undefined is not a function
Связанные ошибки: number is not a function, object is not a function, string is not a function, Unhandled Error: ‘foo’ is not a function, Function Expected
Возникает при попытке вызова значения как функции, когда значение функцией не является. Например:
var foo = undefined;
foo();
Эта ошибка обычно возникает, если вы пытаетесь вызвать функцию для объекта, но опечатались в названии.
var x = document.getElementByID('foo');
Несуществующие свойства объекта по-умолчанию имеют значение undefined
, что приводит к этой ошибке.
Другие вариации, такие как “number is not a function” возникают при попытке вызвать число, как будто оно является функцией.
Как исправить ошибку: убедитесь в корректности имени функции. Для этой ошибки, номер строки обычно указывает в правильное место.
Uncaught ReferenceError: Invalid left-hand side in assignment
Связанные ошибки: Uncaught exception: ReferenceError: Cannot assign to ‘functionCall()’, Uncaught exception: ReferenceError: Cannot assign to ‘this’
Вызвано попыткой присвоить значение тому, чему невозможно присвоить значение.
Наиболее частый пример этой ошибки — это условие в if:
if(doSomething() = 'somevalue')
В этом примере программист случайно использовал один знак равенства вместо двух. Выражение “left-hand side in assignment” относится к левой части знака равенства, а, как можно видеть в данном примере, левая часть содержит что-то, чему нельзя присвоить значение, что и приводит к ошибке.
Как исправить ошибку: убедитесь, что вы не пытаетесь присвоить значение результату функции или ключевому слову this
.
Uncaught TypeError: Converting circular structure to JSON
Связанные ошибки: Uncaught exception: TypeError: JSON.stringify: Not an acyclic Object, TypeError: cyclic object value, Circular reference in value argument not supported
Всегда вызвано циклической ссылкой в объекте, которая потом передается в JSON.stringify
.
var a = { };
var b = { a: a };
a.b = b;
JSON.stringify(a);
Так как a
и b
в примере выше имеют ссылки друг на друга, результирующий объект не может быть приведен к JSON.
Как исправить ошибку: удалите циклические ссылки, как в примере выше, из всех объектов, которые вы хотите сконвертировать в JSON.
Unexpected token ;
Связанные ошибки: Expected ), missing ) after argument list
Интерпретатор JavaScript что-то ожидал, но не обнаружил там этого. Обычно вызвано пропущенными фигурными, круглыми или квадратными скобками.
Токен в данной ошибке может быть разным — может быть написано “Unexpected token ]”, “Expected {” или что-то еще.
Как исправить ошибку: иногда номер строки не указывает на правильное местоположение, что затрудняет исправление ошибки.
Ошибка с [ ] { } ( ) обычно вызвано несовпадающей парой. Проверьте, все ли ваши скобки имеют закрывающую пару. В этом случае, номер строки обычно указывает на что-то другое, а не на проблемный символ.
Unexpected / связано с регулярными выражениями. Номер строки для данного случая обычно правильный.
Unexpected; обычно вызвано символом; внутри литерала объекта или массива, или списка аргументов вызова функции. Номер строки обычно также будет верным для данного случая.
Uncaught SyntaxError: Unexpected token ILLEGAL
Связанные ошибки: Unterminated String Literal, Invalid Line Terminator
В строковом литерале пропущена закрывающая кавычка.
Как исправить ошибку: убедитесь, что все строки имеют правильные закрывающие кавычки.
Uncaught TypeError: Cannot read property ‘foo’ of null, Uncaught TypeError: Cannot read property ‘foo’ of undefined
Связанные ошибки: TypeError: someVal is null, Unable to get property ‘foo’ of undefined or null reference
Попытка прочитать null
или undefined
так, как будто это объект. Например:
var someVal = null;
console.log(someVal.foo);
Как исправить ошибку: обычно вызвано опечатками. Проверьте, все ли переменные, использованные рядом со строкой, указывающей на ошибку, правильно названы.
Uncaught TypeError: Cannot set property ‘foo’ of null, Uncaught TypeError: Cannot set property ‘foo’ of undefined
Связанные ошибки: TypeError: someVal is undefined, Unable to set property ‘foo’ of undefined or null reference
Попытка записать null
или undefined
так, как будто это объект. Например:
var someVal = null;
someVal.foo = 1;
Как исправить ошибку: это тоже обычно вызвано ошибками. Проверьте имена переменных рядом со строкой, указывающей на ошибку.
Uncaught RangeError: Maximum call stack size exceeded
Связанные ошибки: Uncaught exception: RangeError: Maximum recursion depth exceeded, too much recursion, Stack overflow
Обычно вызвано неправильно программной логикой, что приводит к бесконечному вызову рекурсивной функции.
Как исправить ошибку: проверьте рекурсивные функции на ошибки, которые могут вынудить их делать рекурсивные вызовы вечно.
Uncaught URIError: URI malformed
Связанные ошибки: URIError: malformed URI sequence
Вызвано некорректным вызовом decodeURIComponent
.
Как исправить ошибку: убедитесь, что вызовы decodeURIComponent на строке ошибки получают корректные входные данные.
XMLHttpRequest cannot load some/url. No ‘Access-Control-Allow-Origin’ header is present on the requested resource
Связанные ошибки: Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at some/url
Эта проблема всегда связана с использованием XMLHttpRequest.
Как исправить ошибку: убедитесь в корректности запрашиваемого URL и в том, что он удовлетворяет same-origin policy. Хороший способ найти проблемный код — посмотреть на URL в сообщении ошибки и найти его в своём коде.
InvalidStateError: An attempt was made to use an object that is not, or is no longer, usable
Связанные ошибки: InvalidStateError, DOMException code 11
Означает то, что код вызвал функцию, которую нельзя было вызывать в текущем состоянии. Обычно связано c XMLHttpRequest
при попытке вызвать на нём функции до его готовности.
var xhr = new XMLHttpRequest();
xhr.setRequestHeader('Some-Header', 'val');
В данном случае вы получите ошибку потому, что функция setRequestHeader
может быть вызвана только после вызова xhr.open
.
Как исправить ошибку: посмотрите на код в строке, указывающей на ошибку, и убедитесь, что он вызывается в правильный момент или добавляет нужные вызовы до этого (как с xhr.open
).
Заключение
JavaScript содержит в себе одни из самых бесполезных ошибок, которые я когда-либо видел, за исключением печально известной Expected T_PAAMAYIM_NEKUDOTAYIM
в PHP. Большая ознакомленность с ошибками привносит больше ясности. Современные браузеры тоже помогают, так как больше не выдают абсолютно бесполезные ошибки, как это было раньше.
Какие самые непонятные ошибки вы встречали? Делитесь своими наблюдениями в комментариях.
P.S. Этот перевод можно улучшить, отправив PR здесь.
- Table of contents
-
open-xml-templating
/
docxtemplater
Public -
nodejs
/
node-v0.x-archive
Public archive -
nodejs
/
node
Public - Parsing And Serializing Large Objects Using JSONStream In Node.js
JSON Stringify large in-memory javascript object. getting error Invalid String length
JSON.stringify(BigFatObject);
open-xml-templating
/
docxtemplater
Public
var express = require('express'); var bodyParser = require('body-parser'); var app = express(); var fs = require('fs'); var http = require('http'); var Docxtemplater = require('docxtemplater'); var ImageModule = require('docxtemplater-image-module'); var numeral = require('numeral'); var request = require('sync-request'); var cache = require('memory-cache'); var sizeOf = require('image-size'); var entities = require('entities'); var JSZip = require('jszip'); var code128 = { /** * Converts an input string to the equivilant string, that need to be produced using the 'Code 128' font. * * @static * @public * @this Demo.BarcodeConverter128 * @memberof Demo.BarcodeConverter128 * @param {string} value String to be encoded * @return {string} Encoded string start/stop and checksum characters included */ stringToBarcode: function (value) { //value = '08323000000000032943007019' // Parameters : a string // Return : a string which give the bar code when it is dispayed with CODE128.TTF font // : an empty string if the supplied parameter is no good var charPos, minCharPos; var currentChar, checksum; var isTableB = true, isValid = true; var returnValue = ""; if (value.length > 0) { // Check for valid characters for (var charCount = 0; charCount < value.length; charCount++) { //currentChar = char.GetNumericValue(value, charPos); currentChar = value.charCodeAt(charCount);// value.substr(charCount, 1); if (!(currentChar >= 32 && currentChar <= 126)) { isValid = false; break; } } // Barcode is full of ascii characters, we can now process it if (isValid) { charPos = 0; var weight = 1; checksum = 0; while (charPos < value.length) { if (isTableB) { // See if interesting to switch to table C // yes for 4 digits at start or end, else if 6 digits if (charPos === 0 || charPos + 4 === value.length) { minCharPos = 4; } else { minCharPos = 6; } minCharPos = this.isNumber(value, charPos, minCharPos); if (minCharPos < 0) { // Choice table C if (charPos === 0) { // Starting with table C returnValue = String.fromCharCode(237); // char.ConvertFromUtf32(205); } else { // Switch to table C returnValue = returnValue + String.fromCharCode(199) } isTableB = false; } else { if (charPos === 0) { // Starting with table B returnValue = String.fromCharCode(232); // char.ConvertFromUtf32(204); } } } if (!isTableB) { // We are on table C, try to process 2 digits minCharPos = 2; minCharPos = this.isNumber(value, charPos, minCharPos); if (minCharPos < 0) { currentChar = parseInt(value.substr(charPos, 2)); checksum = checksum + (currentChar * weight); weight=weight+1; if ((currentChar <= 94) && (currentChar >= 0)) { currentChar = currentChar + 32 } else if ((currentChar <= 106) && (currentChar >= 95)) { currentChar = currentChar + 32 + 100 } else { currentChar = -1 } returnValue = returnValue + String.fromCharCode(currentChar); charPos += 2; } else { // We haven't 2 digits, switch to table B returnValue = returnValue + String.fromCharCode(231); isTableB = true; } } //if (isTableB) { // // Process 1 digit with table B // returnValue = returnValue + value.substr(charPos, 1); // charPos++; //} } // Calculation of the checksum checksum = checksum + 105; //checksum = 0; //for (var loop = 0; loop < returnValue.length; loop++) { // currentChar = returnValue.charCodeAt(loop);// returnValue.substr(loop, 1); // currentChar = currentChar < 127 ? currentChar - 32 : currentChar - 100; // if (loop === 0) { // checksum = 105; // } // //else { // checksum = (checksum + ((loop+1) * currentChar)) // //} //} var inputvalue = checksum % 103; if ((inputvalue <= 94) && (inputvalue >= 0)) { inputvalue = inputvalue + 32 } else if ((inputvalue <= 106) && (inputvalue >= 95)) { inputvalue = inputvalue + 32+100 } else { inputvalue = -1 } // Add the checksum and the STOP returnValue = returnValue + String.fromCharCode(inputvalue) + String.fromCharCode(238); //var utf8_txt = cp1252_to_utf8(returnValue); } } return returnValue; }, isNumber: function (InputValue, CharPos, MinCharPos) { // if the MinCharPos characters from CharPos are numeric, then MinCharPos = -1 MinCharPos--; if (CharPos + MinCharPos < InputValue.length) { while (MinCharPos >= 0) { //if (parseInt(InputValue.substr(CharPos + MinCharPos, 1)) < 48 || parseInt(InputValue.substr(CharPos + MinCharPos, 1)) > 57) { if (InputValue.charCodeAt(CharPos + MinCharPos) < 48 || InputValue.charCodeAt(CharPos + MinCharPos) > 57) { break; } MinCharPos--; } } return MinCharPos; } }; app.use(bodyParser.json({ limit: '50mb' })); app.use(bodyParser.urlencoded({ limit: '50mb', extended: true })); var expressions = require('angular-expressions'); expressions.filters.num = function (input, digits) { if (input == null) return '0'; else return input.toFixed(digits); }; expressions.filters.drupart = function (input) { if (input == null) { return ''; } else if (input.length <= 14) { return input; } else { var array = input.split('.'); var value = array[1]; return value; } } expressions.filters.currency = function (input) { if (input == null) return '$0.00'; else return numeral(input).format('$0, 0.00'); } expressions.filters.billpay = function (input){ if (input == null) return ""; else { input = input.replace(/ /g, ''); if (input.length == 13) //needs to be a valid bpay code { return '*71 230 ' + input.substr(0, 11) + ' ' + input.substr(11, 2); } else return input; } } expressions.filters.encodebarcode = function (barcode){ if ((barcode == null) || (barcode == '')) return ""; else { var result = code128.stringToBarcode(barcode); result = entities.encodeXML(result); return '<w:p><w:r><w:rPr> <w:rFonts w:ascii="CCode128_S3" w:eastAsia="CCode128_S3" w:hAnsi="CCode128_S3"/><w:color w:val="000000"/><w:sz w:val="24"/></w:rPr><w:t>'+result+'</w:t></w:r></w:p>'; } } expressions.filters.dmsbcode = function (barcode){ if (barcode == "") return ""; else return '<w:p><w:r><w:rPr><w:rFonts w:ascii="DMSBCode"/><w:sz w:val="56" /></w:rPr><w:t>' + barcode + '</w:t></w:r></w:p>'; } angularParser = function (tag) { expr = expressions.compile(tag); return { get: expr }; } //setup image options var opts = {}; opts = { centered: false } opts.getImage = function (imgData, tagName) { //var imgHttp = 'http://webapps.parra.catholic.edu.au/intweb2/crest-for-fas/bw/' + imgData; var imgHttp = 'http://webapps.parra.catholic.edu.au/intweb2/crest-for-fas/bw/' + imgData var _result = cache.get(imgData); if (_result == null) { _result = (request('GET', imgHttp).getBody()); cache.put(imgData, _result); }; return _result; } opts.getSize = function (img, tagValue, tag) { var _result = cache.get("s" + tagValue); if (_result == null) { _result = sizeOf(img); cache.put("s" + tagValue, _result); } return [_result.width, _result.height]; } app.use(function (req, res, next) { res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); next(); }); app.get('/node/matthew/', function (req, res) { //app.get('/', function (req, res) { res.send('To merge your document make a POST request to /Merger'); }); app.post('/node/matthew/merger', function (req, res) { //app.post('/merger', function (req, res) { var template = __dirname + "\templates\" + req.body.templateName; var content = fs.readFileSync(template, "binary"); var imageModule = new ImageModule(opts); //var doc = new Docxtemplater(content); var zip = new JSZip(content); var doc = new Docxtemplater().loadZip(zip) //use angular expressions doc.setOptions({ parser: angularParser }) doc.attachModule(imageModule); var merge = { letters: req.body.data, freeText: req.body.freeText, hasFreeText: req.body.hasFreeText, hasSummary: req.body.hasSummary, sortSummary: req.body.sortSummary, schoolSummary: req.body.schoolSummary, newPage: '<w:p><w:br w:type="page" /></w:p>' }; doc.setData(merge); //doc.render(); try { doc.render(); } catch (error) { console.log(JSON.stringify({error: error})); } var buf = doc.getZip() .generate({ type: "nodebuffer", compression: 'DEFLATE' }); res.json({ blob: buf.toString('base64') }); //}); }); var server = app.listen(process.env.PORT, function () { //var server = app.listen(1337, function () { var host = server.address().address; var port = server.address().port; });
diff --git a/es6/modules/loop.js b/es6/modules/loop.js index cae4645..66a179b 100644 --- a/es6/modules/loop.js +++ b/es6/modules/loop.js @@ -53,7 +53,20 @@ const loopModule = { )); } options.scopeManager.loopOver(part.value, loopOver, part.inverted); - return {value: totalValue.join("")}; + let result = ""; + // console.log(JSON.stringify({"parts.length": parts.length})); + for (var i = 0, len = totalValue.length; i < len; i++) { + // if (result.length > 10000) { + // console.log(JSON.stringify({"result.length": result.length})); + // } + if (result.length > 1000000) { + console.log(JSON.stringify({"result.length": result.length})); + + } + result+= totalValue[i]; + } + return {value: result}; + // return {value: totalValue.join("")}; }, }; diff --git a/es6/tests/speed.js b/es6/tests/speed.js index 9d69a8a..3b2c69b 100644 --- a/es6/tests/speed.js +++ b/es6/tests/speed.js @@ -62,4 +62,20 @@ describe("speed test", function () { expect(duration).to.be.below(20000); }); } + + it.only("should be fast for many iterations", function () { + let innerContent = ""; + for (let i = 1; i <= 1000; i++) { + innerContent+="a"; + } + const content = `<w:t>{#users}${innerContent}{name}{/users}</w:t>`; + const users = []; + for (let i = 1; i <= 1000000; i++) { + users.push({name: "foo"}); + } + const time = new Date(); + testUtils.createXmlTemplaterDocx(content, {tags: {users}}).render(); + const duration = new Date() - time; + expect(duration).to.be.below(600); + }); });
nodejs
/
node-v0.x-archive
Public archive
JSON.stringify(monsterously_HUGE_object)
nodejs
/
node
Public
<--- JS stacktrace —-> ==== JS stack trace ========================================= Security context: 0x3b902a9266a1 <JS Object> 1: fromString(aka fromString) [buffer.js:~194] [pc=0x1b9dcb6f3ad0](this=0x2f5cceb02311 <undefined>,string=0x866865dc389 <Very long string[14371]>,encoding=0x3b902a92f309 <String[4]: utf8>) 2: from [buffer.js:~96] [pc=0x1b9dcb6c9936](this=0x2bea42122009 <JS Function Buffer (SharedFunctionInfo 0x3b902a94b481)>,value=0x86 6865dc389 <Very long string[14371]>,encodingOrOffset=0x3b902a92f309 <... FATAL ERROR: CALL_AND_RETRY_LAST All
fs.writeFileSync('file.json', JSON.stringify(hugeObject), 'utf16le');
console.log('*'.repeat(Math.pow(2, 28) - 16).length); console.log(JSON.stringify('*'.repeat(Math.pow(2, 28) - 16 - 1)).length);
268435440 test.js:5 console.log(JSON.stringify('*'.repeat(Math.pow(2, 28) - 16 - 1)).length); ^ RangeError: Invalid string length at JSON.stringify (<anonymous>) at Object.<anonymous> (test.js:5:18) at Module._compile (module.js:569:30) at Object.Module._extensions..js (module.js:580:10) at Module.load (module.js:503:32) at tryModuleLoad (module.js:466:12) at Function.Module._load (module.js:458:3) at Function.Module.runMain (module.js:605:10) at startup (bootstrap_node.js:158:16) at bootstrap_node.js:575:3
console.log('*'.repeat(Math.pow(2, 28) - 16).length); console.log(JSON.stringify('*'.repeat(Math.pow(2, 28) - 16 - 2)).length);
Add `buffer.constants`, containing length limits for `Buffer` and `string` instances. This could be useful for programmers to tell whether a value can be turned into a string or not. Ref: nodejs#13465
Add `buffer.constants`, containing length limits for `Buffer` and `string` instances. This could be useful for programmers to tell whether a value can be turned into a string or not. Ref: #13465 PR-URL: #13467 Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Vse Mozhet Byt <[email protected]> Reviewed-By: Tobias Nießen <[email protected]>
Add `buffer.constants`, containing length limits for `Buffer` and `string` instances. This could be useful for programmers to tell whether a value can be turned into a string or not. Ref: #13465 PR-URL: #13467 Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Vse Mozhet Byt <[email protected]> Reviewed-By: Tobias Nießen <[email protected]>
Add `buffer.constants`, containing length limits for `Buffer` and `string` instances. This could be useful for programmers to tell whether a value can be turned into a string or not. Ref: #13465 PR-URL: #13467 Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Vse Mozhet Byt <[email protected]> Reviewed-By: Tobias Nießen <[email protected]>
Add `buffer.constants`, containing length limits for `Buffer` and `string` instances. This could be useful for programmers to tell whether a value can be turned into a string or not. Ref: #13465 PR-URL: #13467 Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Vse Mozhet Byt <[email protected]> Reviewed-By: Tobias Nießen <[email protected]>
Add `buffer.constants`, containing length limits for `Buffer` and `string` instances. This could be useful for programmers to tell whether a value can be turned into a string or not. Ref: #13465 PR-URL: #13467 Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Vse Mozhet Byt <[email protected]> Reviewed-By: Tobias Nießen <[email protected]>
Add `buffer.constants`, containing length limits for `Buffer` and `string` instances. This could be useful for programmers to tell whether a value can be turned into a string or not. Ref: #13465 PR-URL: #13467 Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Vse Mozhet Byt <[email protected]> Reviewed-By: Tobias Nießen <[email protected]>
fs.writeFile(reportFilename, JSON.stringify(report, null, 2), err => { if (err) throw err; });
Parsing And Serializing Large Objects Using JSONStream In Node.js
// Require the core node modules. var chalk = require( "chalk" ); var fileSystem = require( "fs" ); var JSONStream = require( "JSONStream" ); // ----------------------------------------------------------------------------------- // // ----------------------------------------------------------------------------------- // // Imagine that we are performing some sort of data migration and we have to move data // from one database to flat files; then transport those flat files elsewhere; then, // import those flat files into a different database. var records = [ { id: 1, name: "Terminator" }, { id: 2, name: "Predator" }, { id: 3, name: "True Lies" }, { id: 4, name: "Running Man" }, { id: 5, name: "Twins" } // .... hundreds of thousands of records .... ]; // If the record-sets are HUGE, then we run the risk of running out of memory when // serializing the data as JSON: // -- // RangeError: Invalid string length (aka, out-of-memory error) // -- // As such, we're going to STREAM the record-set to a data file using JSONStream. The // .stringify() method creates a TRANSFORM (or THROUGH) stream to which we will write // the individual records in the record-set. var transformStream = JSONStream.stringify(); var outputStream = fileSystem.createWriteStream( __dirname + "/data.json" ); // In this case, we're going to pipe the serialized objects to a data file. transformStream.pipe( outputStream ); // Iterate over the records and write EACH ONE to the TRANSFORM stream individually. // -- // NOTE: If we had tried to write the entire record-set in one operation, the output // would be malformed - it expects to be given items, not collections. records.forEach( transformStream.write ); // Once we've written each record in the record-set, we have to end the stream so that // the TRANSFORM stream knows to output the end of the array it is generating. transformStream.end(); // Once the JSONStream has flushed all data to the output stream, let's indicate done. outputStream.on( "finish", function handleFinish() { console.log( chalk.green( "JSONStream serialization complete!" ) ); console.log( "- - - - - - - - - - - - - - - - - - - - - - -" ); } ); // ----------------------------------------------------------------------------------- // // ----------------------------------------------------------------------------------- // // Since the stream actions are event-driven (and asynchronous), we have to wait until // our output stream has been closed before we can try reading it back in. outputStream.on( "finish", function handleFinish() { // When we read in the Array, we want to emit a "data" event for every item in // the serialized record-set. As such, we are going to use the path "*". var transformStream = JSONStream.parse( "*" ); var inputStream = fileSystem.createReadStream( __dirname + "/data.json" ); // Once we pipe the input stream into the TRANSFORM stream, the parser will // start working it's magic. We can bind to the "data" event to handle each // top-level item as it is parsed. inputStream .pipe( transformStream ) // Each "data" event will emit one item in our record-set. .on( "data", function handleRecord( data ) { console.log( chalk.red( "Record (event):" ), data ); } ) // Once the JSONStream has parsed all the input, let's indicate done. .on( "end", function handleEnd() { console.log( "- - - - - - - - - - - - - - - - - - - - - - -" ); console.log( chalk.green( "JSONStream parsing complete!" ) ); } ) ; } );
Next Lesson PHP Tutorial
JSON stringification is the process of converting a Javascript object to a flat JSON string that can be used inside a program. In Javascript, the standard way to do this is by using the method JSON.stringify()
, as the Javascript standard specifies.
JSON.stringify()
is the opposite of JSON.parse(), which converts JSON into Javascript objects. This article and Udacity’s JSON.parse()
article have similar structures to emphasize the close relationship between the two functions.
Using JSON.stringify()
Databases, REST APIs, and other data stores usually don’t have a built-in ability to interpret Javascript. When they encounter Javascript objects, they must convert those objects to a format they can handle and that is also easily readable by other programs. JSON.stringify()
does that conversion.
JSON.stringify()
acts as a bridge between Javascript objects and the data sources that hold them, converting the objects into “flat” strings that contain the object’s key-value pairs. Strings are universally understood by all data stores.
A flat JSON string looks like this:
let example1 = '{"a": "b", "c": 1, "d": {"e": 2}}';
Required Parameter: Javascript Object
JSON.stringify()
requires one parameter: the Javascript object to be converted into a JSON string. Javascript object literals can be cumbersome to type, so it is often easier and more readable to pass them as a variable. Since modern Javascript considers everything, even simple data values, to be a form of object, JSON.stringify()
can also take simple structures like numbers and arrays as its required parameter.
// Create a Javascript object variable for JSON.stringify() to use
let example2a = {
a: 1,
b: 2
};
let example2b = JSON.stringify(example2a);
console.log(example2b); // {"a": 1, "b": 2};
Optional Parameter: Replacer
JSON.stringify()
takes an optional second parameter which is called a “replacer.” A replacer is similar to a reviver function in JSON.parse()
. It handles Javascript values that JSON.stringify()
does not automatically process itself.
A replacer is the only way to include certain types of values in a stringified Javascript object, such as a function, undefined value, or Symbol. Depending on a browser’s Javascript implementation, these values may be changed to null or completely omitted.
This example of a replacer function handles functions by replacing them with a boilerplate string since functions lose their scope and rarely work properly after stringification.
function replacer(key, value) {
if(value typeof "function") { return "function"; }
return value;
}
Optional Parameter: White Space
JSON.stringify()
also takes an optional third parameter called “space.” This parameter, which must be a String
or Number
object, defines the amount of white space inserted into the stringified JSON for readability.
// JSON without space inserted for readability
{"a": "b", "c": 1, "d": {"e": 2}}
// JSON with 4 spaces inserted for readability with "space" parameter
{
"a": "b",
"c": 1,
"d":
{
"e": 2
}
}
Whether defining white space by a string or by a number, the maximum amount of white space characters possible is 10. If a white space string is longer than 10 characters, only the first 10 characters are used. Numbers less than 1 result in no white space insertion.
You can only use the white space parameter if you have defined a replacer function as well. Assuming a Javascript object called “object” and a replacer function called “replacer,” the following examples show valid JSON.stringify()
calls including the white space parameter.
JSON.stringify(object, replacer, new Number(10)); // 10 spaces
JSON.stringify(object, replacer, new Number(100)); // 10 spaces
JSON.stringify(object, replacer, new Number(-2)); // 0 spaces
JSON.stringify(object, replacer, " "); // 5 spaces
Handling JSON.stringify() Special Cases with Replacers
Error Handling
Like JSON.parse(), JSON.stringify() may throw errors, so it should be wrapped in a try catch statement. The function throws a TypeError in two contexts: if a circular reference occurs in the Javascript object or if the Javascript object contains a BigInt. A replacer cannot catch or change these errors.
try {
JSON.stringify(input);
} catch (e) {
return undefined; // Or whatever error-handling you want here
}
Non-enumerable Properties
In the context of JSON.stringify()
, non-enumerable properties are those that you can convert into a string without losing important contextual information. Non-enumerable properties in Javascript include functions, because they lose their scope when stringified, and Symbol objects, because they may not stringify properly due to encoding and locale differences.
Since non-enumerable properties are usually uninformative in a string, you can often replace them with boilerplate text by using a replacer, as mentioned earlier. Non-enumerable properties will not stop the rest of a Javascript object from being stringified unless they cause the errors mentioned earlier.
Symbol Keys and Values
If a Javascript object contains a key that is a Symbol
object (which is sometimes necessary for text in other languages), JSON.stringify()
ignores both the key and its value. This means that a key-value pair like this never enters a replacer, so developers should assume that pairs like this will never show up in stringified objects.
When Symbol
objects are Javascript object values, you can work around them as you would with other non-enumerable objects.
if(value typeof "symbol") { return "symbol"; }
Using JSON.stringify() Safely
Wrap JSON.stringify()
in a function to cover its limits, handing special cases appropriate to your needs. Include a replacer function in a closure to handle non-enumerable properties.
If you want to do some kind of special stringification outside of what JSON.stringify()
provides, add a second replacer function as a parameter to your safe stringifying function. Call the second replacer function at the end of the default replacer function, as shown below.
You can also add a white space parameter to your function and provide a default value.
Putting all of this together with previous code examples, here’s a simple safe Javascript object stringification function:
function safeJsonStringify(json, replacer, space) {
function defaultReplacer(key, value) {
if(value typeof "function") { return "function"; }
if(value typeof "symbol") { return "symbol"; }
if(typeof value === 'undefined') { return null; }
if(replacer !== undefined) { replacer(); }
}
try {
let defaultSpace = 4;
if(space !== undefined) { defaultSpace = space; }
let jsonString = JSON.stringify(json, defaultReplacer, defaultSpace);
} catch(e) {
return null;
}
}
Conclusion
JSON.stringify()
works similarly to JSON.parse()
— together they provide a complete solution to transferring Javascript objects to a storable format and back again. Many programming techniques, including SQL database programming, rely on the safe usage of both functions in tandem to move information around.
Enroll in our Intro to Programming Nanodegree Program today to learn more about JSON.stringify()
and other programming concepts.