JavaScript метод JSON.stringify()
JavaScript JSONОпределение и применение
JavaScript метод .stringify() объекта JSON является глобальной функцией, предназначенной для преобразования элементарных значений, объектов, или массивов в строку в формате JSON (сериализует объект, массив или элементарное значение).
В качестве возвращаемого значения вы можете получить строку в формате JSON, которую дополнительно можно отфильтровать и отформатировать для удобства чтения.
Для того, чтобы произвести анализ (парсинг) строк в формате JSON и при необходимости преобразовать и возвратить значения, полученные в ходе анализа, то вы можете воспользоваться глобальной функцией JSON.parse().
Поддержка браузерами
Метод | Chrome | Firefox | Opera | Safari | IExplorer | Edge |
---|---|---|---|---|---|---|
JSON.stringify() | Да | Да | Да | Да | 8.0 | Да |
JavaScript синтаксис:
JSON.stringify(value); JSON.stringify(value, replacer); JSON.stringify(value, replacer, space); value - Any replacer - Function space - String, или Number
Спецификация
JavaScript 1.7Значения параметров
Параметр | Описание |
---|---|
value | Значение, которое преобразуется в строку JSON. Обязательный параметр. |
replacer | Функция, которая изменяет поведение процесса сериализации, или массив имен свойств, подлежащих сериализации (служат в качестве белого списка для выбора / фильтрации включаемых значений в строку JSON). Если это значение равно null или не указано, то все свойства объекта включаются в результирующую строку JSON. Функция может принимать два параметра - ключ и значение, которое будет преобразовано. Функция будет вызываться для каждого значения, подлежащего сериализации. Ключевое слово this внутри этой функции будет ссылаться на объект или массив, в котором определено текущее значение. Первым аргументом функции замены (ключ) будет передаваться имя свойства объекта, или индекс в массиве для текущего значения, а во втором само значение, которое будет замещено возвращаемым значением функции. Если функция вернет undefined, или вообще ничего не вернет, то это значение (и соответствующее ему свойство объекта или элемент массива) будет исключено из сериализации. Если во втором аргументе передать массив строк (при передаче чисел они преобразуются в строки), то эти строки будут использоваться как имена свойств объекта. В этом случае любое свойство, имя которого отсутствует в массиве, будет исключено из сериализации. Кроме того, возвращаемая строка будет содержать свойства в том же порядке, в каком они перечислены в массиве. Необязательный параметр. |
space | Строковое, или числовое значение (объект String, или Number), используемое для вставки пробелов в выходную строку JSON для удобства чтения. Если это числовое значение, то оно задает количество пробелов перед каждым преобразованным свойством. Числовое значение ограничено значением 10, если оно больше, то значение равно 10, значения меньше 1 указывают на то, что пространство не должно использоваться. Если в качестве значения используется строка, то она используется в качестве пробела. Количество символов в строке ограничено числом 10, если оно больше, то строка усекается до десяти символов. Если этот параметр не указан, или его значение равно null, то дополнительные пробелы не используются. Необязательный параметр. |
Исключения
Тип исключения | Описание |
---|---|
TypeError | Возникает в том случае, если в переданном значении обнаружена циклическая ссылка. |
Пример использования
Преобразование значений в строку JSON
В следующем примере мы с Вами рассмотрим ситуацию, когда метод JSON.stringify() вызывается с одним аргументом:
JSON.stringify({}); // сериализуем переданное значение "{}" JSON.stringify({ a: 1, b:2 }); // сериализуем переданное значение "{"a":1,"b":2}" JSON.stringify(true); // сериализуем переданное значение "true" JSON.stringify("foo"); // сериализуем переданное значение ""foo"" JSON.stringify(["str", true, 500, null]); // сериализуем переданное значение "["str",true,500,null]" JSON.stringify([NaN, null, Infinity, -Infinity]); // сериализуем переданное значение "[null,null,null,null]" const person = { // инициализируем переменную содержащую объект "id": 500, "single": false, "firstName": "Иван", "lastName": "Иванов", "phoneNumbers": [ 88000001111, 88000001112 ] } JSON.stringify(person); // сериализуем переданное значение "{"id":500,"single":false,"firstName":"Иван","lastName":"Иванов","phoneNumbers":[88000001111,88000001112]}"
Объекты Boolean, Number и String преобразуются в соответствующие примитивные значения во время преобразования. Обратите внимание, что в последнем примере числовые значения NaN и Infinity как и само значение null преобразуются в null.
Сериализация неперечислимых свойств
В следующем примере мы с Вами рассмотрим как происходит сериализация объектов, которые содержат неперечислимые свойства:
// создаем объект и инициализируем переменную этим значением let myObj = Object.create( null, { // объект-прототип соответствует null a: { // добавляем новое свойство value: 1 // указываем значение свойства }, b: { // добавляем новое свойство value: 2, // указываем значение свойства enumerable: true // указываем, что свойство перечислимо } }); // выводим в консоль значение переменной console.log( myObj ) // {a: 1, b: 2} JSON.stringify( myObj ); // сериализуем переданное значение "{"b":2}"
В этом примере с использованием метода create() мы создали новый объект с указанным объектом прототипом null и двумя собственными (неунаследованными) свойствами, описываемые дескрипторами.
Ключ (атрибут) value устанавливает значение для свойств, а атрибут enumerable, указанный для свойства "b" соответствует логическому значению, которое определяет отображается ли данное свойство при перечислении свойств объекта в котором оно содержится в цикле for..in, или при вызове метода keys(), который возвращает массив, содержащий имена всех собственных перечислимых (неунаследованных) свойств указанного объекта. Если указано true, то свойство перечисляется, если false, то нет (это значение по умолчанию при добавлении свойств, которые описываются объектом дескрипотором).
Обратите внимание на важный момент неперечислимые свойства объекта не будут сериализованы! Подробную информацию о дескрипторах свойств вы можете получить в описании метода defineProperty(), который позволяет определить новое или изменить существующее свойство объекта, описывая его дескрипторами.
Кроме того, если вы используете в качестве индекса массива строковое значение, то этот элемент массива не будет перечислим, а так же не будет сериализован:
// инициализируем переменную содержащую массив let arr = ["a", 1, true]; arr["something"] = "zzz"; // используем в качестве индекса массива строковое значение и присваиваем значение // выводим в консоль значение переменной console.log( arr ); // ["a", 1, true, something: "zzz"] JSON.stringify(arr); // сериализуем переданное значение "["a",1,true]"
Сериализация экземпляров класса
Когда передается объект, массив, строка, число, логическое значение или значение null сериализация выполняется очень просто, однако, когда значение для сериализации содержит объекты, являющиеся экземплярами класса, то процесс сериализации усложняется. Давайте рассмотрим на следующих примерах:
JSON.stringify([new Number(1), new String("some string"), new Boolean(true)]); // сериализуем переданное значение "[1,"some string",true]" JSON.stringify(new Date(2019, 6, 7)); // сериализуем переданное значение ""2019-07-06T21:00:00.000Z"" JSON.stringify(["str", 10, function(){}, undefined, () => {}]); // сериализуем переданное значение "["str",10,null,null,null]" JSON.stringify(function(){}); // сериализуем переданное значение undefined JSON.stringify(undefined); // сериализуем переданное значение undefined // Стандартные структуры данных JSON.stringify([new Set([1]), new Map([[2, 3]]), new WeakSet([{a: 14}]), new WeakMap([[{a: 5}, 6]])]); // сериализуем переданное значение "[{},{},{},{}]" // Типизированные массивы JSON.stringify([new Int8Array([1]), new Int16Array([2]), new Int32Array([3])]); // сериализуем переданное значение "[{"0":1},{"0":2},{"0":3}]" JSON.stringify([new Uint8Array([1]), new Uint8ClampedArray([2]), new Uint16Array([3]), new Uint32Array([4])]); // сериализуем переданное значение "[{"0":1},{"0":2},{"0":3},{"0":4}]" JSON.stringify([new Float32Array([1]), new Float64Array([2])]); // сериализуем переданное значение "[{"0":1},{"0":2}]"
Обратите внимание на то, что экземпляры объекта Date реализуют метод toJSON(), возвращая строку (такую же, как Date.toISOString()), таким образом, они рассматриваются как строки. Более подробно об использовании метода toJSON() вы можете узнать в следующем разделе с примерами. Метод JSON.stringify() также может просто возвращать undefined при передаче таких "чистых" значений как function(){}, или undefined.
Все остальные экземпляры объектов (включая Map, Set, WeakMap и WeakSet) будут иметь только сериализованные перечислимые свойства. Если функция, или символ не определены, то при преобразовании они либо пропускаются (если они найдены в объекте), либо преобразуются в null (если они найдены в массиве). Более подробно сериализация символов будет рассмотрена в следующем примере:
JSON.stringify({ a: [Symbol("a"), Symbol("b")] }); // сериализуем переданное значение "{"a":[null,null]}" JSON.stringify({ a: 1, b: 2, c: Symbol("") }); // сериализуем переданное значение "{"a":1,"b":2}" JSON.stringify({ [Symbol("a")]: "a" }); // сериализуем переданное значение "{}" JSON.stringify({ [Symbol.for("a")]: "a" }, [Symbol.for("b")]); // сериализуем переданное значение "{}" JSON.stringify({ [Symbol.for("c")]: "c" }, function(k, v) { // сериализуем переданное значение с переданной функцией if (typeof k === "symbol") { return "a symbol"; } }); undefined
Обратите внимание на важный момент, что все свойства с символьным ключом будут полностью игнорироваться, даже при использовании функции, которая изменяет поведение процесса сериализации (второй аргумент метода JSON.stringify()).
Преобразование объектов, содержащих метод toJSON()
Если функция JSON.stringify() встретит объект содержащий метод toJSON(), то она вызовет этот метод и выполнит сериализацию полученного в результате этого вызова значения, а не самого переданного объекта. Метод toJSON() вызывается с единственным строковым аргументом, в котором передается имя свойства объекта, или индекс массива, и отвечает за определение того какие данные будут сериализованы.
Класс Date определяет метод toJSON(), преобразующий объекты Date в строки с помощью метода Date.toISOString(). Обращаю Ваше внимание на то, что никакие другие встроенные в язык JavaScript классы не определяют метод toJSON(), но при необходимости вы можете определить его в своих классах.
// возвращаем произведение свойств этого объекта JSON.stringify({ a: 2, b: 5, toJSON(){return this.a * this.b} }); "10" // возвращаем объект без изменений JSON.stringify({ first: 1, second: 2, toJSON(){return this} }); "{"first":1,"second":2}"
Важным моментом является то, что метод toJSON() не выполняет сериализацию объекта, а возвращает лишь значение, которое будет сериализовано вместо оригинального объекта.
Сериализация объектов с дополнительной функцией
Второй необязательный параметр функции JSON.stringify() позволяет добавить в процесс сериализации дополнительный слой фильтрации. Он может быть как фильтрующей функцией, так и массивом, который выступает в роли белого списка для выбора. Если передать во втором параметре функцию, то она будет использоваться как инструмент замены, подобно методу toJSON(), рассмотренному в предыдущем примере.
Давайте рассмотрим использование второго параметра функции JSON.stringify() на следующих примерах:
// использование массива для фильтрации JSON.stringify([{id: 100, name: "Vasya"}, {id: 200, name: "Dima"}], ["id"]); // сериализуем переданное значение (выбираем только ключи id) "[{"id":100},{"id":200}]" // использование массива для фильтрации 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; // возвращаем значение }; 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; // возвращаем значение }; 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!!!"}"
В этом примере мы рассмотрели различные примеры использования второго аргумента функции JSON.stringify(). В первых двух примерах мы использовали массив в качестве значения аргумента, который выступает в роли белого списка для выбора и включает в результ только те ключи, которые содержит массив, а в следующих примерах было рассмотрено использование фильтрующей функции.
Управление интервалом строки JSON
В следующем примере мы с Вами рассмотрим использование третьего аргумента метода JSON.stringify(), который позволяет управлять интервалом в возвращаемой строке JSON:
// инициализируем переменную, содержащую объект const obj = {a: 1, b: false, c: "3"}; const jsonString = JSON.stringify(obj, null, 10); // интервал соответствует десяти пробелам console.log( jsonString ); // выводим в консоль значение переменной "{ "a": 1, "b": false, "c": "3" }" const jsonString2 = JSON.stringify(obj, null, '\t'); // интервал соответствует табуляции console.log( jsonString2 ); // выводим в консоль значение переменной "{ "a": 1, "b": false, "c": "3" }" const jsonString3 = JSON.stringify(obj, null, 'xxxxxx'); // интервал соответствует строковому значению console.log( jsonString3 ); // выводим в консоль значение переменной "{ xxxxxx"a": 1, xxxxxx"b": false, xxxxxx"c": "3" }"
В этом примере мы рассмотрели различные возможности формотирования строк в формате JSON, использовали как числовое значение, которое определяет количество пробелов перед каждым преобразованным свойством, использовали специальный символ табуляции и произвольной строковое значение. Учтите тот момент, что при использовании произвольного строкового значения обратное преобразование с помощью метода JSON.parse() будет невозможно, по той причине, что значение не будет валидной JSON строкой.
Глубокое копирование объекта
Глубокое копирование объекта в отличии от поверхностного дублирует каждый встреченный ею объект, то есть копия и исходный объект не будут разделять общие элементы, поэтому она в действительности будет являться полноценной копией оригинала.
Давайте рассмотрим как с помощью методов JSON.parse() и JSON.stringify() сделать глубокую копию нашего объекта:
// инициализируем переменную, содержащую объект 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() анализируем эту строку и возвращаем новый объект, соответствующий переданной строке. В результате чего, свойства, значением которых является объект имеют собственные значения, а не ссылаются на первоначальный объект. Благодаря этим преобразованиям мы получаем новый объект, который является действительно полноценной копией оригинала.
JavaScript JSON