Формат JSON, метод toJSON
Допустим, у нас есть сложный объект, и мы хотели бы преобразовать его в строку, чтобы отправить по сети или просто вывести для логирования.
Естественно, такая строка должна включать в себя все важные свойства.
Мы могли бы реализовать преобразование следующим образом:
let user = < name: "John", age: 30, toString() < return `", age: $>`; > >; alert(user); //
…Но в процессе разработки добавляются новые свойства, старые свойства переименовываются и удаляются. Обновление такого toString каждый раз может стать проблемой. Мы могли бы попытаться перебрать свойства в нём, но что, если объект сложный, и в его свойствах имеются вложенные объекты? Мы должны были бы осуществить их преобразование тоже.
К счастью, нет необходимости писать код для обработки всего этого. У задачи есть простое решение.
JSON.stringify
JSON (JavaScript Object Notation) – это общий формат для представления значений и объектов. Его описание задокументировано в стандарте RFC 4627. Первоначально он был создан для JavaScript, но многие другие языки также имеют библиотеки, которые могут работать с ним. Таким образом, JSON легко использовать для обмена данными, когда клиент использует JavaScript, а сервер написан на Ruby/PHP/Java или любом другом языке.
JavaScript предоставляет методы:
- JSON.stringify для преобразования объектов в JSON.
- JSON.parse для преобразования JSON обратно в объект.
Например, здесь мы преобразуем через JSON.stringify данные студента:
let student = < name: 'John', age: 30, isAdmin: false, courses: ['html', 'css', 'js'], wife: null >; let json = JSON.stringify(student); alert(typeof json); // мы получили строку! alert(json); /* выведет объект в формате JSON: < "name": "John", "age": 30, "isAdmin": false, "courses": ["html", "css", "js"], "wife": null >*/
Метод JSON.stringify(student) берёт объект и преобразует его в строку.
Полученная строка json называется JSON-форматированным или сериализованным объектом. Мы можем отправить его по сети или поместить в обычное хранилище данных.
Обратите внимание, что объект в формате JSON имеет несколько важных отличий от объектного литерала:
- Строки используют двойные кавычки. Никаких одинарных кавычек или обратных кавычек в JSON. Так ‘John’ становится «John» .
- Имена свойств объекта также заключаются в двойные кавычки. Это обязательно. Так age:30 становится «age»:30 .
JSON.stringify может быть применён и к примитивам.
JSON поддерживает следующие типы данных:
- Объекты
- Массивы [ . ]
- Примитивы:
- строки,
- числа,
- логические значения true/false ,
- null .
// число в JSON остаётся числом alert( JSON.stringify(1) ) // 1 // строка в JSON по-прежнему остаётся строкой, но в двойных кавычках alert( JSON.stringify('test') ) // "test" alert( JSON.stringify(true) ); // true alert( JSON.stringify([1, 2, 3]) ); // [1,2,3]
JSON является независимой от языка спецификацией для данных, поэтому JSON.stringify пропускает некоторые специфические свойства объектов JavaScript.
- Свойства-функции (методы).
- Символьные ключи и значения.
- Свойства, содержащие undefined .
let user = < sayHi() < // будет пропущено alert("Hello"); >, [Symbol("id")]: 123, // также будет пропущено something: undefined // как и это - пропущено >; alert( JSON.stringify(user) ); // <> (пустой объект)
Обычно это нормально. Если это не то, чего мы хотим, то скоро мы увидим, как можно настроить этот процесс.
Самое замечательное, что вложенные объекты поддерживаются и конвертируются автоматически.
let meetup = < title: "Conference", room: < number: 23, participants: ["john", "ann"] >>; alert( JSON.stringify(meetup) ); /* вся структура преобразована в строку: < "title":"Conference", "room":, > */
Важное ограничение: не должно быть циклических ссылок.
let room = < number: 23 >; let meetup = < title: "Conference", participants: ["john", "ann"] >; meetup.place = room; // meetup ссылается на room room.occupiedBy = meetup; // room ссылается на meetup JSON.stringify(meetup); // Ошибка: Преобразование цикличной структуры в JSON
Здесь преобразование завершается неудачно из-за циклической ссылки: room.occupiedBy ссылается на meetup , и meetup.place ссылается на room :
Исключаем и преобразуем: replacer
Полный синтаксис JSON.stringify :
let json = JSON.stringify(value, [replacer, space])
value Значение для кодирования. replacer Массив свойств для кодирования или функция соответствия function(key, value) . space Дополнительное пространство (отступы), используемое для форматирования.
В большинстве случаев JSON.stringify используется только с первым аргументом. Но если нам нужно настроить процесс замены, например, отфильтровать циклические ссылки, то можно использовать второй аргумент JSON.stringify .
Если мы передадим ему массив свойств, будут закодированы только эти свойства.
let room = < number: 23 >; let meetup = < title: "Conference", participants: [, ], place: room // meetup ссылается на room >; room.occupiedBy = meetup; // room ссылается на meetup alert( JSON.stringify(meetup, ['title', 'participants']) ); // <"title":"Conference","participants":[<>,<>]>
Здесь мы, наверное, слишком строги. Список свойств применяется ко всей структуре объекта. Так что внутри participants – пустые объекты, потому что name нет в списке.
Давайте включим в список все свойства, кроме room.occupiedBy , из-за которого появляется цикличная ссылка:
let room = < number: 23 >; let meetup = < title: "Conference", participants: [, ], place: room // meetup ссылается на room >; room.occupiedBy = meetup; // room ссылается на meetup alert( JSON.stringify(meetup, ['title', 'participants', 'place', 'name', 'number']) ); /* < "title":"Conference", "participants":[,], "place": > */
Теперь всё, кроме occupiedBy , сериализовано. Но список свойств довольно длинный.
К счастью, в качестве replacer мы можем использовать функцию, а не массив.
Функция будет вызываться для каждой пары (key, value) , и она должна возвращать заменённое значение, которое будет использоваться вместо исходного. Или undefined , чтобы пропустить значение.
В нашем случае мы можем вернуть value «как есть» для всего, кроме occupiedBy . Чтобы игнорировать occupiedBy , код ниже возвращает undefined :
let room = < number: 23 >; let meetup = < title: "Conference", participants: [, ], place: room // meetup ссылается на room >; room.occupiedBy = meetup; // room ссылается на meetup alert( JSON.stringify(meetup, function replacer(key, value) < alert(`$: $`); return (key == 'occupiedBy') ? undefined : value; >)); /* пары ключ:значение, которые приходят в replacer: : [object Object] title: Conference participants: [object Object],[object Object] 0: [object Object] name: John 1: [object Object] name: Alice place: [object Object] number: 23 occupiedBy: [object Object] */
Обратите внимание, что функция replacer получает каждую пару ключ/значение, включая вложенные объекты и элементы массива. И она применяется рекурсивно. Значение this внутри replacer – это объект, который содержит текущее свойство.
Первый вызов – особенный. Ему передаётся специальный «объект-обёртка»: . Другими словами, первая (key, value) пара имеет пустой ключ, а значением является целевой объект в общем. Вот почему первая строка из примера выше будет «:[object Object]» .
Идея состоит в том, чтобы дать как можно больше возможностей replacer – у него есть возможность проанализировать и заменить/пропустить даже весь объект целиком, если это необходимо.
Форматирование: space
Третий аргумент в JSON.stringify(value, replacer, space) – это количество пробелов, используемых для удобного форматирования.
Ранее все JSON-форматированные объекты не имели отступов и лишних пробелов. Это нормально, если мы хотим отправить объект по сети. Аргумент space используется исключительно для вывода в удобочитаемом виде.
Ниже space = 2 указывает JavaScript отображать вложенные объекты в несколько строк с отступом в 2 пробела внутри объекта:
let user = < name: "John", age: 25, roles: < isAdmin: false, isEditor: true >>; alert(JSON.stringify(user, null, 2)); /* отступ в 2 пробела: < "name": "John", "age": 25, "roles": < "isAdmin": false, "isEditor": true >> */ /* для JSON.stringify(user, null, 4) результат содержит больше отступов: < "name": "John", "age": 25, "roles": < "isAdmin": false, "isEditor": true >> */
Третьим аргументом также может быть строка. В этом случае строка будет использоваться для отступа вместо ряда пробелов.
Параметр space применяется исключительно для логирования и красивого вывода.
Пользовательский «toJSON»
Как и toString для преобразования строк, объект может предоставлять метод toJSON для преобразования в JSON. JSON.stringify автоматически вызывает его, если он есть.
let room = < number: 23 >; let meetup = < title: "Conference", date: new Date(Date.UTC(2017, 0, 1)), room >; alert( JSON.stringify(meetup) ); /* < "title":"Conference", "date":"2017-01-01T00:00:00.000Z", // (1) "room": // (2) > */
Как видим, date (1) стал строкой. Это потому, что все объекты типа Date имеют встроенный метод toJSON , который возвращает такую строку.
Теперь давайте добавим собственную реализацию метода toJSON в наш объект room (2) :
let room = < number: 23, toJSON() < return this.number; >>; let meetup = < title: "Conference", room >; alert( JSON.stringify(room) ); // 23 alert( JSON.stringify(meetup) ); /* < "title":"Conference", "room": 23 >*/
Как видите, toJSON используется как при прямом вызове JSON.stringify(room) , так и когда room вложен в другой сериализуемый объект.
JSON.parse
Чтобы декодировать JSON-строку, нам нужен другой метод с именем JSON.parse.
let value = JSON.parse(str, [reviver]);
str JSON для преобразования в объект. reviver Необязательная функция, которая будет вызываться для каждой пары (ключ, значение) и может преобразовывать значение.
// строковый массив let numbers = "[0, 1, 2, 3]"; numbers = JSON.parse(numbers); alert( numbers[1] ); // 1
Или для вложенных объектов:
let user = '< "name": "John", "age": 35, "isAdmin": false, "friends": [0,1,2,3] >'; user = JSON.parse(user); alert( user.friends[1] ); // 1
JSON может быть настолько сложным, насколько это необходимо, объекты и массивы могут включать другие объекты и массивы. Но они должны быть в том же JSON-формате.
Вот типичные ошибки в написанном от руки JSON (иногда приходится писать его для отладки):
let json = `< name: "John", // Ошибка: имя свойства без кавычек "surname": 'Smith', // Ошибка: одинарные кавычки в значении (должны быть двойными) 'isAdmin': false, // Ошибка: одинарные кавычки в ключе (должны быть двойными) "birthday": new Date(2000, 2, 3), // Ошибка: не допускается конструктор "new", только значения "gender": "male" // Ошибка: отсутствует запятая после непоследнего свойства "friends": [0,1,2,3], // Ошибка: не должно быть запятой после последнего свойства >`;
Кроме того, JSON не поддерживает комментарии. Добавление комментария в JSON делает его недействительным.
Существует ещё один формат JSON5, который поддерживает ключи без кавычек, комментарии и т.д. Но это самостоятельная библиотека, а не спецификация языка.
Обычный JSON настолько строг не потому, что его разработчики ленивы, а потому, что позволяет легко, надёжно и очень быстро реализовывать алгоритм кодирования и чтения.
Использование reviver
Представьте, что мы получили объект meetup с сервера в виде строки данных.
// title: (meetup title), date: (meetup date) let str = '';
…А теперь нам нужно десериализовать её, т.е. снова превратить в объект JavaScript.
Давайте сделаем это, вызвав JSON.parse :
let str = ''; let meetup = JSON.parse(str); alert( meetup.date.getDate() ); // Ошибка!
Значением meetup.date является строка, а не Date объект. Как JSON.parse мог знать, что он должен был преобразовать эту строку в Date ?
Давайте передадим JSON.parse функцию восстановления вторым аргументом, которая возвращает все значения «как есть», но date станет Date :
let str = ''; let meetup = JSON.parse(str, function(key, value) < if (key == 'date') return new Date(value); return value; >); alert( meetup.date.getDate() ); // 30 - теперь работает!
Кстати, это работает и для вложенных объектов:
let schedule = `< "meetups": [ , ] >`; schedule = JSON.parse(schedule, function(key, value) < if (key == 'date') return new Date(value); return value; >); alert( schedule.meetups[1].date.getDate() ); // 18 - отлично!
Итого
- JSON – это формат данных, который имеет собственный независимый стандарт и библиотеки для большинства языков программирования.
- JSON поддерживает простые объекты, массивы, строки, числа, логические значения и null .
- JavaScript предоставляет методы JSON.stringify для сериализации в JSON и JSON.parse для чтения из JSON.
- Оба метода поддерживают функции преобразования для интеллектуального чтения/записи.
- Если объект имеет метод toJSON , то он вызывается через JSON.stringify .
Задачи
Преобразуйте объект в JSON, а затем обратно в обычный объект
важность: 5
Преобразуйте user в JSON, затем прочитайте этот JSON в другую переменную.
let user = < name: "Василий Иванович", age: 35 >;
Формат JSON в JavaScript (stringify, parse)
В JavaScript есть инструменты для работы с данными в формате JSON. Этот формат удобен для хранения и передачи данных на сервер. JSON — это всего лишь способ хранения данных, которая является простой строкой. Но внутри этой строки записаны данные и значения, которые похожи по своей структуре на «объект». Если Вы уже работали с PHP, то аналогией для JSON может быть функция seriallize( ).
Создание JSON в JavaScript (stringify)
Метод JSON.stringify позволяет преобразовать объект JavaScipt в формат JSON. Давайте попробуем сделать такое преобразование, используя все типы переменных для демонстрации:
var a = < 'BOOL': true, 'INT': 20, 'ARRAY': [1, 2, 3], 'STRING': 'Тише, мыши, кот на крыше' >; var b = JSON.stringify(a); console.log(b);
В результате выполнения этого примера в консоли появится строчка:
В этой строчке можно увидеть все данные, которые были внутри объекта, который был преобразован в примере в JSON формат. Теперь эти данные можно отправить на сервер. Это может быть сервер с сайтом, написанным на языке программирования PHP, потому что у PHP есть встроенная функция для работы с форматом JSON. Там полученную строку можно будет преобразовать обратно в объект и обращаться к ним, как к элементам объекта.
Чтение JSON в JavaScript (parse)
Если необходимо обработать (прочитать) данные в формате JSON, то нужно взять строку (то есть данные, закодированные в JSON формате) и применить метод JSON.parse. Как только данные расшифруются, то можно будет обращаться к ним как к элементам объекта. Попробуем метод на практике, возъмём полученную в предыдущем примере JSON строку:
var a = ''; var b = JSON.parse(a); console.log(b['STRING']); console.log(b['ARRAY'][0]); console.log(b['BOOL']);
При выполнении такого кода будут преобразованы данные из строки в формате JSON в объект, и к ним можно будет обратиться напрямую. Поэтому в консоли появятся строчки:
Тише, мыши, кот на крыше 1 true
JSON.stringify()
Метод JSON.stringify() возвращает JavaScript-значение, преобразованное в JSON-строку. Возможна замена значений, если как replacer передана функция, или включение только определённых свойств, если как replacer указан массив.
Синтаксис
JSON.stringify(value[, replacer[, space]])
Параметры
Значение, преобразуемое в строку JSON.
Если является функцией, преобразует значения и свойства по ходу их преобразования в строку; если является массивом, определяет набор свойств, включаемых в объект в окончательной строке. Подробное описание функции replacer даётся в статье Использование родного объекта JSON (en-US) руководства по JavaScript.
Делает результат красиво отформатированным (расставляя пробелы).
Описание
Метод JSON.stringify() преобразует значение в представляющую его нотацию JSON со следующими оговорками:
- Порядок преобразования в строку свойств объектов, не являющихся массивами, не гарантируется. Не полагайтесь на порядок свойств во время преобразования.
- Объекты Boolean , Number и String преобразуются в соответствующие примитивные значения, в соответствии с традиционным соглашением о семантике преобразований.
- Значение undefined , функция или символ, встреченные во время преобразования, будут либо опущены (если они найдены в объекте), либо превращены в null (если они найдены в массиве).
- Все свойства, имеющие ключ в виде символа, будут полностью проигнорированы, даже при использовании функции replacer .
JSON.stringify(>); // '<>' JSON.stringify(true); // 'true' JSON.stringify("foo"); // '"foo"' JSON.stringify([1, "false", false]); // '[1,"false",false]' JSON.stringify( x: 5 >); // '' JSON.stringify( x: 5, y: 6 >); // '' or '' JSON.stringify([new Number(1), new String("false"), new Boolean(false)]); // '[1,"false",false]' // Символы: 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" >, function (k, v) if (typeof k === "symbol") return "символ"; > >); // '<>'
Параметр space
Параметр space может использоваться для управления форматированием отступов в конечной строке. Если он числовой, каждый последующий уровень вложенности будет дополнен отступом из пробелов, количество которых соответствует уровню (вплоть до десятого уровня). Если он строковый, каждый последующий уровень вложенности будет предваряться этой строкой (или её первыми десятью символами).
JSON.stringify( a: 2 >, null, " "); // ' // "a": 2 // >'
Использование символа табуляции имитирует стандартное красивое форматирование:
JSON.stringify( uno: 1, dos: 2 >, null, "\t"); // вернёт строку: // ' // "uno": 1, // "dos": 2 // >'
Поведение метода toJSON()
Если преобразуемый в строку объект имеет свойство с именем toJSON и значением свойства, установленным в функцию, то этот метод toJSON() изменит стандартное поведение преобразования в JSON: вместо преобразования объекта будет использоваться значение, возвращаемое методом toJSON() . Например:
Предупреждение: Функции не являются допустимыми данными в JSON, так что они не будут работать. Кроме того, некоторые объекты, например, объект Date , станут строками после применения JSON.parse() .
var obj = foo: "foo", toJSON: function () return "bar"; >, >; JSON.stringify(obj); // '"bar"' JSON.stringify( x: obj >); // ''
Пример использования метода JSON.stringify() вместе с localStorage
В случае, если вы хотите сохранить объект таким образом, чтобы его можно было восстановить даже после закрытия браузера, имеет смысл воспользоваться следующим примером, демонстрирующим применение метода JSON.stringify() :
// Создаём некоторый объект var session = screens: [], state: true, >; session.screens.push( name: "экранА", width: 450, height: 250 >); session.screens.push( name: "экранБ", width: 650, height: 350 >); session.screens.push( name: "экранВ", width: 750, height: 120 >); session.screens.push( name: "экранГ", width: 250, height: 60 >); session.screens.push( name: "экранД", width: 390, height: 120 >); session.screens.push( name: "экранЕ", width: 1240, height: 650 >); // Преобразуем его в строку JSON с помощью метода JSON.stringify(), // затем сохраняем его в localStorage под именем session localStorage.setItem("session", JSON.stringify(session)); // Пример того, как можно преобразовать строку, полученную с помощью метода // JSON.stringify() и сохранённую в localStorage обратно в объект var restoredSession = JSON.parse(localStorage.getItem("session")); // Переменная restoredSession содержит объект, который был сохранён // в localStorage console.log(restoredSession);
Пример использования параметра replacer
var foo = foundation: "Mozilla", model: "box", week: 45, transport: "car", month: 7, >; JSON.stringify(foo, function (key, value) if (typeof value === "string") return undefined; // удаляем все строковые свойства > return value; >); // '' JSON.stringify(foo, ["week", "month"]); // '', сохранились только свойства week и month
Спецификации
Specification ECMAScript Language Specification
# sec-json.stringifyСовместимость с браузерами
BCD tables only load in the browser
Смотрите также
- Использование родного объекта JSON (en-US)
- JSON.parse()
JSON.stringify()
Обычно JSON используется для обмена данными с сервером.
При отправке на сервер данные всегда передаются в виде строки.
Чтобы преобразовать объект JavaScript в строку, используется функция JSON.stringify().
Преобразование объекта JavaScript в строку
Допустим, что у нас есть следующий объект JavaScript:
var obj = < "name":"John", "age":30, "city":"New York">;
Воспользуемся JavaScript функцией JSON.stringify() и преобразуем его в строку:
var myJSON = JSON.stringify(obj);
В результате у нас будет строка, записанная по правилам JSON.
Теперь myJSON — готовая к отправке на сервер строка:
var obj = < "name":"John", "age":30, "city":"New York">; var myJSON = JSON.stringify(obj); document.getElementById("demo").innerHTML = myJSON;
Преобразование массива JavaScript в строку
Также, мы можем преобразовывать массивы JavaScript в JSON строки.
Допустим, что у нас есть следующий объект JavaScript:
var arr = [ "John", "Peter", "Sally", "Jane" ];
Воспользуемся JavaScript функцией JSON.stringify() и преобразуем его в строку:
var myJSON = JSON.stringify(arr);
В результате у нас будет строка, записанная по правилам JSON.
Теперь myJSON — готовая к отправке на сервер строка:
var arr = [ "John", "Peter", "Sally", "Jane" ]; var myJSON = JSON.stringify(arr); document.getElementById("demo").innerHTML = myJSON;
Преобразование дат в строку
Объекты даты и времени (тип Date) нельзя использовать в JSON. Функция JSON.stringify() позволяет преобразовать любой объект даты в JSON строку.
var obj = < "name":"John", "today":new Date(), "city":"New York">; var myJSON = JSON.stringify(obj); document.getElementById("demo").innerHTML = myJSON;
При получении данных с сервера вы сможете преобразовать строку в объект даты обратно.
Преобразование функций в строку
Функции нельзя использовать в JSON.
Функция JSON.stringify() удалит любую функцию из объекта JavaScript, и ключ, и значение:
var obj = < "name":"John", "age":function () , "city":"New York">; var myJSON = JSON.stringify(obj); document.getElementById("demo").innerHTML = myJSON;
Этого можно избежать, если преобразовать функцию в строку до того, как использовать функцию JSON.stringify().
var obj = < "name":"John", "age":function () , "city":"New York">; obj.age = obj.age.toString(); var myJSON = JSON.stringify(obj); document.getElementById("demo").innerHTML = myJSON;
Внимание! Следует избегать использования функций в JSON, так как в этом случае теряется их область видимости, а для обратного преобразования приходится использовать функцию eval(), что нежелательно.
Поддержка браузерами
Функция JSON.stringify() включена во все основные браузеры и в последний стандарт ECMAScript (JavaScript).