JavaScript метод JSON.stringify()
Определение и применение
JavaScript метод .stringify() объекта
JSON
является статическим методом, предназначенным для преобразования элементарных значений, объектов, или массивов в
строку в формате JSON (сериализует объект, массив или элементарное значение).
В качестве возвращаемого значения вы можете получить строку в формате JSON, либо undefined для
корневых undefined, функции или символа. Строку можно отфильтровать и отформатировать для удобства
чтения.
Для того, чтобы произвести анализ (парсинг) строк в формате JSON и при необходимости преобразовать и возвратить
значения, полученные в ходе анализа, то вы можете воспользоваться статическим методом
JSON.parse().
Поддержка браузерами
Спецификация: ecma262
Поддерживается во всех основных браузерах с 2015 года.
| Возможность | Компьютеры | Мобильные | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
stringify | 3 | 12 | 3.5 | 10.5 | 4 | 18 | 4 | 11 | 4 | 1.0 | 4.4 | 4 |
| Strings are escaped to well-formed UTF-8 | 72 | 79 | 64 | 60 | 12.1 | 72 | 64 | 50 | 12.2 | 11.0 | 72 | 12.2 |
поддерживаетсяс ограниченияминет поддержкинет данных≤31 — не позднее версии 31
JavaScript синтаксис:
JSON.stringify(value);
JSON.stringify(value, replacer);
JSON.stringify(value, replacer, space);Спецификация
ECMAScript — JSON.stringify(). Метод стандартизован в ECMAScript 5.
Значения параметров
| Параметр | Описание |
|---|---|
value |
Значение, которое преобразуется в строку JSON. Обязательный параметр. |
replacer |
Функция, которая изменяет поведение процесса сериализации, или массив имен свойств, подлежащих сериализации
(служат в качестве белого списка для выбора / фильтрации включаемых значений в строку JSON). Если это
значение равно null или не указано, то собственные перечислимые свойства со строковыми ключами
включаются в результирующую строку JSON.Функция может принимать два параметра - ключ и значение, которое будет преобразовано. Функция будет вызываться для каждого значения, подлежащего сериализации. Ключевое слово this внутри этой функции будет ссылаться на объект или массив, в котором определено текущее
значение.Первым аргументом функции замены (ключ) будет передаваться имя свойства объекта, или индекс в массиве для текущего значения, а во втором само значение, которое будет замещено возвращаемым значением функции. Если функция вернет undefined, или вообще ничего не вернет, то свойство объекта будет пропущено,
элемент массива станет null, а для корневого значения результатом будет undefined.
Первый вызов replacer обрабатывает корень с ключом пустой строкой. Если во
втором аргументе передать массив строк (при передаче чисел они преобразуются в строки), то эти строки
будут использоваться как имена свойств объекта. В этом случае любое свойство, имя которого отсутствует в
массиве, будет исключено из сериализации. Кроме того, возвращаемая строка будет содержать свойства в том же
порядке, в каком они перечислены в массиве.
Необязательный параметр.
|
space |
Строковое, или числовое значение (объект
String, или
Number), используемое для вставки пробелов в выходную строку JSON для удобства чтения. Если это числовое
значение, то оно задает количество пробелов перед каждым преобразованным свойством. Числовое значение
ограничено значением 10, если оно больше, то значение равно 10, значения меньше
1 указывают на то, что пространство не должно использоваться.Если в качестве значения используется строка, то она используется в качестве пробела. Количество символов в строке ограничено числом 10, если оно больше, то строка усекается до десяти символов. Если этот параметр не указан, или его значение равно null, то дополнительные пробелы не используются.
Необязательный параметр.
|
Исключения
| Тип исключения | Описание |
|---|---|
TypeError |
Возникает при циклической ссылке или BigInt, если toJSON() либо replacer не
преобразовали проблемное значение.
|
Примеры, попробуйте сами
Преобразование значений в строку JSON
В следующем примере мы с Вами рассмотрим ситуацию, когда метод JSON.stringify() вызывается с одним
аргументом:
Как значения JavaScript превращаются в JSON
Измените исходные значения или аргументы метода и нажмите ▶. Сравните результат в консоли.
index.html
<p>Результат выполнения выводится в консоль.</p>
main.js
console.log(JSON.stringify({})) // сериализуем переданное значение
// "{}"
console.log(JSON.stringify({ a: 1, b: 2 })) // сериализуем переданное значение
// "{"a":1,"b":2}"
console.log(JSON.stringify(true)) // сериализуем переданное значение
// "true"
console.log(JSON.stringify('foo')) // сериализуем переданное значение
// ""foo""
console.log(JSON.stringify(['str', true, 500, null])) // сериализуем переданное значение
// "["str",true,500,null]"
console.log(JSON.stringify([NaN, null, Infinity, -Infinity])) // сериализуем переданное значение
// "[null,null,null,null]"
const person = {
// инициализируем переменную содержащую объект
id: 500,
single: false,
firstName: 'Иван',
lastName: 'Иванов',
phoneNumbers: [88000001111, 88000001112],
}
console.log(JSON.stringify(person)) // сериализуем переданное значение
// "{"id":500,"single":false,"firstName":"Иван","lastName":"Иванов","phoneNumbers":[88000001111,88000001112]}"
Объекты Boolean, Number и String преобразуются в соответствующие примитивные
значения во время преобразования.
Обратите внимание, что в последнем примере числовые значения NaN и Infinity как и само
значение null преобразуются в null.
Сериализация неперечислимых свойств
В следующем примере мы с Вами рассмотрим как происходит сериализация объектов, которые содержат неперечислимые свойства:
Почему неперечислимое свойство не попадает в JSON
Измените исходные значения или аргументы метода и нажмите ▶. Сравните результат в консоли.
index.html
<p>Результат выполнения выводится в консоль.</p>
main.js
// создаем объект и инициализируем переменную этим значением
let myObj = Object.create(null, {
// объект-прототип соответствует null
a: {
// добавляем новое свойство
value: 1, // указываем значение свойства
},
b: {
// добавляем новое свойство
value: 2, // указываем значение свойства
enumerable: true, // указываем, что свойство перечислимо
},
})
// оба свойства существуют, но перечислимо только b
console.log(Object.getOwnPropertyNames(myObj)) // ["a","b"]
console.log(Object.keys(myObj)) // ["b"]
console.log(JSON.stringify(myObj)) // сериализуем переданное значение
// "{"b":2}"
В этом примере с использованием метода
create()
мы создали новый объект с указанным объектом прототипом null и двумя собственными (неунаследованными)
свойствами, описываемые дескрипторами.
Ключ (атрибут) value устанавливает значение для свойств, а атрибут enumerable, указанный для
свойства "b" соответствует логическому значению, которое определяет отображается ли данное свойство при
перечислении свойств объекта в котором оно содержится в цикле for..in, или при вызове метода
keys(), который возвращает массив, содержащий имена всех собственных перечислимых (неунаследованных) свойств указанного
объекта. Если указано true, то свойство перечисляется, если false, то нет (это значение по
умолчанию при добавлении свойств, которые описываются объектом дескрипотором).
Обратите внимание на важный момент неперечислимые свойства объекта не будут сериализованы! Подробную информацию о дескрипторах свойств вы можете получить в описании метода defineProperty(), который позволяет определить новое или изменить существующее свойство объекта, описывая его дескрипторами.
Дополнительное свойство массива с именем, не являющимся индексом, может быть перечислимым, но JSON.stringify() его игнорирует. Сериализуются позиции от 0 до length − 1:
Почему JSON не сохраняет дополнительные свойства массива
Измените исходные значения или аргументы метода и нажмите ▶. Сравните результат в консоли.
index.html
<p>Результат выполнения выводится в консоль.</p>
main.js
// инициализируем переменную содержащую массив
let arr = ['a', 1, true]
arr['something'] = 'zzz' // используем в качестве индекса массива строковое значение и присваиваем значение
// свойство something есть у массива, но не является индексом
console.log(Object.keys(arr)) // ["0","1","2","something"]
console.log(arr.something, arr.length) // zzz 3
console.log(JSON.stringify(arr)) // сериализуем переданное значение
// "["a",1,true]"
Сериализация экземпляров класса
Когда передается объект, массив, строка, число, логическое значение или значение null сериализация
выполняется очень просто, однако, когда значение для сериализации содержит объекты, являющиеся экземплярами класса, то
процесс сериализации усложняется. Давайте рассмотрим на следующих примерах:
Как JSON преобразует даты и специальные объекты
Измените исходные значения или аргументы метода и нажмите ▶. Сравните результат в консоли.
index.html
<p>Результат выполнения выводится в консоль.</p>
main.js
console.log(JSON.stringify([new Number(1), new String('some string'), new Boolean(true)])) // сериализуем переданное значение
// "[1,"some string",true]"
console.log(JSON.stringify(new Date('2019-07-07T00:00:00.000Z'))) // сериализуем переданное значение
// ""2019-07-07T00:00:00.000Z""
console.log(JSON.stringify(['str', 10, function () {}, undefined, () => {}])) // сериализуем переданное значение
// "["str",10,null,null,null]"
console.log(JSON.stringify(function () {})) // сериализуем переданное значение
// undefined
console.log(JSON.stringify(undefined)) // сериализуем переданное значение
// undefined
// Стандартные структуры данных
console.log(JSON.stringify([new Set([1]), new Map([[2, 3]]), new WeakSet([{ a: 14 }]), new WeakMap([[{ a: 5 }, 6]])])) // сериализуем переданное значение
// "[{},{},{},{}]"
// Типизированные массивы
console.log(JSON.stringify([new Int8Array([1]), new Int16Array([2]), new Int32Array([3])])) // сериализуем переданное значение
// "[{"0":1},{"0":2},{"0":3}]"
console.log(
JSON.stringify([new Uint8Array([1]), new Uint8ClampedArray([2]), new Uint16Array([3]), new Uint32Array([4])])
) // сериализуем переданное значение
// "[{"0":1},{"0":2},{"0":3},{"0":4}]"
console.log(JSON.stringify([new Float32Array([1]), new Float64Array([2])])) // сериализуем переданное значение
// "[{"0":1},{"0":2}]"
Обратите внимание на то, что экземпляры объекта
Date
реализуют метод
toJSON(), возвращая строку (такую же, как
Date.prototype.toISOString()), таким образом, они рассматриваются как строки. Более подробно об использовании метода toJSON() вы
можете узнать в следующем разделе с примерами. Метод JSON.stringify() также может просто возвращать
undefined при передаче таких "чистых" значений как function(){}, или undefined.
Все остальные экземпляры объектов (включая Map, Set, WeakMap и
WeakSet) будут иметь только сериализованные перечислимые свойства. Значения
undefined, функции и символы при преобразовании либо пропускаются (если они найдены в объекте), либо
преобразуются в null (если они найдены в массиве). Более подробно сериализация символов будет рассмотрена
в следующем примере:
Как JSON обрабатывает символы в ключах и значениях
Измените исходные значения или аргументы метода и нажмите ▶. Сравните результат в консоли.
index.html
<p>Результат выполнения выводится в консоль.</p>
main.js
console.log(JSON.stringify({ a: [Symbol('a'), Symbol('b')] })) // сериализуем переданное значение
// "{"a":[null,null]}"
console.log(JSON.stringify({ a: 1, b: 2, c: Symbol('') })) // сериализуем переданное значение
// "{"a":1,"b":2}"
console.log(JSON.stringify({ [Symbol('a')]: 'a' })) // сериализуем переданное значение
// "{}"
console.log(JSON.stringify({ [Symbol.for('a')]: 'a' }, [Symbol.for('b')])) // сериализуем переданное значение
// "{}"
console.log(
JSON.stringify({ [Symbol.for('c')]: 'c' }, function (k, v) {
// сериализуем переданное значение с переданной функцией
if (typeof k === 'symbol') {
return 'a symbol'
}
return v // Сохраняем корень; символьный ключ не попадёт в replacer.
})
)
// "{}"
Преобразование объектов, содержащих метод toJSON()
Если функция JSON.stringify() встретит объект содержащий метод toJSON(), то она вызовет этот
метод и выполнит сериализацию полученного в результате этого вызова значения, а не самого переданного объекта. Метод
toJSON() вызывается с единственным строковым аргументом, в котором передается имя свойства объекта, или
индекс массива, и отвечает за определение того какие данные будут сериализованы.
Класс Date определяет метод toJSON(), преобразующий объекты Date в строки с
помощью метода Date.prototype.toISOString(). Для некорректной даты
Date.prototype.toJSON() возвращает null. Метод toJSON() можно определить в собственном
классе; для корневого объекта он получает пустую строку в качестве ключа.
Как toJSON() задаёт представление объекта
Измените исходные значения или аргументы метода и нажмите ▶. Сравните результат в консоли.
index.html
<p>Результат выполнения выводится в консоль.</p>
main.js
// возвращаем произведение свойств этого объекта
console.log(
JSON.stringify({
a: 2,
b: 5,
toJSON() {
return this.a * this.b
},
})
)
// "10"
// возвращаем объект без изменений
console.log(
JSON.stringify({
first: 1,
second: 2,
toJSON() {
return this
},
})
)
// "{"first":1,"second":2}"
Сериализация объектов с дополнительной функцией
Второй необязательный параметр функции JSON.stringify() позволяет добавить в процесс сериализации
дополнительный слой фильтрации. Он может быть как фильтрующей функцией, так и массивом, который выступает в роли
белого списка для выбора. Если передать во втором параметре функцию, то она будет использоваться как инструмент
замены, подобно методу toJSON(), рассмотренному в предыдущем примере.
Давайте рассмотрим использование второго параметра функции JSON.stringify() на следующих примерах:
Как replacer отбирает и заменяет значения
Измените исходные значения или аргументы метода и нажмите ▶. Сравните результат в консоли.
index.html
<p>Результат выполнения выводится в консоль.</p>
main.js
// использование массива для фильтрации
console.log(
JSON.stringify(
[
{ id: 100, name: 'Vasya' },
{ id: 200, name: 'Dima' },
],
['id']
)
) // сериализуем переданное значение (выбираем только ключи id)
// "[{"id":100},{"id":200}]"
// использование массива для фильтрации
console.log(
JSON.stringify(
[
{ id: 100, name: 'Vasya' },
{ id: 200, name: 'Dima' },
],
['name']
)
) // сериализуем переданное значение (выбираем только ключи name)
// "[{"name":"Vasya"},{"name":"Dima"}]"
// использование фильтрующей функции
function replacer(key, val) {
if (key === 'a' || key === 'd') {
// если ключ содержит значение "a" или "d"
return undefined // то исключаем из выборки
}
return val // возвращаем значение
}
console.log(JSON.stringify({ a: 1, b: 2, c: 3, d: 4 }, replacer)) // сериализуем переданное значение с фильтрующей функцией
// "{"b":2,"c":3}"
// использование фильтрующей функции
function replacer2(key, val) {
if (typeof val === 'number') {
// если значение является числом
return val < 4 ? val : undefined // если число меньше 4, то добавляем, если нет то исключаем
}
if (typeof val === 'string') {
// если значение является строкой
return 'it`s a string!!!' // возвращаем новое строковое значение
}
return val // возвращаем значение
}
console.log(JSON.stringify({ a: 1, b: 2, c: { d: 3, e: 4, f: 'str' }, g: 'str' }, replacer2)) // сериализуем переданное значение с фильтрующей функцией
// "{"a":1,"b":2,"c":{"d":3,"f":"it`s a string!!!"},"g":"it`s a string!!!"}"
// Пропуск значения в массиве сохраняет позицию в виде null.
console.log(JSON.stringify([1, 2], (key, value) => (value === 2 ? undefined : value)))
// BigInt и циклическая ссылка требуют явного преобразования.
const cyclic = {}
cyclic.self = cyclic
for (const value of [1n, cyclic]) {
try {
console.log(JSON.stringify(value))
} catch (error) {
console.log(error.name)
}
}
В этом примере мы рассмотрели различные примеры использования второго аргумента функции
JSON.stringify(). В первых двух примерах мы использовали массив в качестве значения аргумента,
который выступает в роли белого списка для выбора и включает в результат только те ключи, которые содержит массив, а в
следующих примерах было рассмотрено использование фильтрующей функции.
Управление интервалом строки JSON
В следующем примере мы с Вами рассмотрим использование третьего аргумента метода JSON.stringify(),
который позволяет управлять интервалом в возвращаемой строке JSON:
Как сделать JSON удобным для чтения
Измените исходные значения или аргументы метода и нажмите ▶. Сравните результат в консоли.
index.html
<p>Результат выполнения выводится в консоль.</p>
main.js
const object = { a: 1, b: false, c: '3' }
console.log(JSON.stringify(object, null, 2)) // Отступ в два пробела.
console.log(JSON.stringify(object, null, '\t')) // Табуляция.
В этом примере мы рассмотрели различные возможности форматирования строк в формате JSON, использовали как числовое значение, которое определяет количество пробелов перед каждым преобразованным свойством, использовали специальный символ табуляции и строковый отступ.
Глубокое копирование объекта
Глубокое копирование создаёт независимые вложенные объекты. Связка JSON.parse(JSON.stringify(obj)) подходит только для
JSON-совместимых данных: она теряет undefined, функции, символы и прототипы, превращает
Date в строку, а NaN и бесконечности — в null. Циклические ссылки и
BigInt без специального преобразования вызывают TypeError. Для поддерживаемых типов и
циклических ссылок используйте structuredClone(); функции он также не копирует.
Давайте рассмотрим как с помощью методов
JSON.parse()
и JSON.stringify() сделать глубокую копию нашего объекта:
Изменение оригинала не затрагивает JSON-копию
Измените исходные значения или аргументы метода и нажмите ▶. Сравните результат в консоли.
index.html
<p>Результат выполнения выводится в консоль.</p>
main.js
// инициализируем переменную, содержащую объект
let obj = {
a: 1,
b: {
c: 2,
},
}
// осуществляем глубокое копирование объекта и инициализируем переменную
let newObj = JSON.parse(JSON.stringify(obj))
console.log(newObj) // переменная содержит значение { a: 1, b: { c: 2} }
obj.b.c = 200 // изменяем значение свойства объекта obj
console.log(obj) // переменная содержит значение { a: 1, b: { c: 200} }
console.log(newObj) // переменная содержит значение { a: 1, b: { c: 2} }
В этом примере с использованием метода JSON.stringify() мы преобразовали значение переменной
obj в строку JSON, а с помощью метода
JSON.parse()
анализируем эту строку и возвращаем новый объект, соответствующий переданной строке. В результате чего, свойства,
значением которых является объект имеют собственные значения, а не ссылаются на первоначальный объект. Для показанного
JSON-совместимого объекта получается независимая копия вложенных данных.