Валерий Шестернин
JSON является популярным форматом обмена данными в веб-разработке по ряду причин: это и простота в использовании, легковесность, гибкость, но главное что он поддерживается подавляющим большинством современных языков программирования и нативно интегрирован в JavaScript. Сейчас сложно представить веб разработку без JSON и в данной статье мы рассмотрим основные приемы работы с данным форматом, а так же примеры использования.
Синтаксис JSON и примеры использования
JSON (JavaScript Object Notation) очень похож на буквенный синтаксис объекта JavaScript с тем лишь отличием, что имена свойств всегда заключены в двойные кавычки, и не используются другие виды кавычек (одинарные, обратные). Кроме того данные в формате JSON нельзя сразу использовать в JavaScript. Для работы с ними у глобального объекта JSON существует два метода: JSON.stringify()
, который при базовом использовании принимает данные с типом Object, Array, Number, String, Boolean и Null, после чего преобразует их в JSON строку и JSON.parse()
, который преобразует данные из JSON.
const myArr = [{ first: 1 }, { second: 2 }];
const myObj = {
myArr: myArr,
myNum: 1,
myStr: "Hello!",
myBoolean: false,
};
const myJSON = JSON.stringify(myObj);
//{"myArr":[{"first":1},{"second":2}],"myNum":1,"myStr":"Hello!","myBoolean":false}
console.log(typeof myJSON); //string
const parsedObj = JSON.parse(myJSON);
//получаем исходный объект со всеми вложениями
console.log(typeof parsedObj);//object
Перевод данных в формат JSON и обратно используется не только для передачи данных по сети, но и для клонирования ссылочных типов данных. В нашем примере свойство myArr объекта parsedObj не будет ссылаться на объявленный в первой строке массив, а создаст его независимую копию.
Ограничения формата JSON и как их обойти
При описании метода JSON.stringify()
я не просто так перечислил поддерживаемые типы данных. Дело в том, что JSON не умеет работать с типами Date, BigInt, Map и остальными, не попавшими в описаный список. Что бы преобразовать такие данные в JSON строку необходимо привести их к поддерживаемому типу. Сделать это можно прямо внутри метода, передав в него необязательным параметром функцию, которая принимает все пары ключ-значение и преобразует их по ходу работы метода.
const myObj = {
bigInt: 123n,
date: new Date("2023-08-27"),
};
console.log(JSON.stringify(myObj));//выдаст ошибку
//TypeError: Do not know how to serialize a BigInt
const replacer = (key, value) => {
if (typeof value === "bigint") {
return Number(value);
}//проверяем тип значения
if (key === "date") {
return value.toString();
}//или ключ
return value;//остальные значения просто возвращаем
};
const replacedObj = JSON.stringify(myObj, replacer);
//{"bigInt":123,"date":"2023-08-27T00:00:00.000Z"}
Кроме того JSON.stringify()
не может работать с циклическими зависимостями. Что бы исключить из сериализации значения с такими зависимостями или если вам нужно преобразовать в JSON формат только часть объекта, можно вместо функции передать вторым параметром массив ключей и в результате метод вернет строку, содержащую только пары ключ-значение с этими ключами.
const person = { name: "John", age: "22" };
const company = { name: "Company", employers: [person] }; //company ссылается на person
person.company = company; //person ссылается на company
console.log(JSON.stringify(person));
//TypeError: Converting circular structure to JSON
const safetyProperties = Object.keys(person).filter(
(elem) => elem !== "company"
); //создаем массив "безопасных" ключей
console.log(JSON.stringify(person, safetyProperties)); //{"name":"John","age":"22"}
Дополнительные возможности JSON.
Метод parse()
глобального объекта JSON тоже принимает дополнительным параметром функцию, которая по аналогии с JSON.stringify()
обработает каждую пару ключ-значение при парсинге строки.
const JSONdata = JSON.stringify({ name: "john", age: 22 });
const capitalizeName = (key, value) => {
if (key === "name") {
capitalizedName = value.replace(value[0], value[0].toUpperCase());
return capitalizedName;
} //если ключ - name возвращаем имя с заглавной буквой в начале
return value; //остальные значения просто возвращаем как есть
};
console.log(JSON.parse(JSONdata, capitalizeName)); //{ name: 'John', age: 22 }
//заменить функцию на массив ключей, как в JSON.stringify не получится
console.log(JSON.parse(JSONdata, ["name"])); //{ name: 'john', age: 22 }
Кроме функции обработки пар ключ-значение, метод JSON.stringify()
может принимать еще одним параметром вид отступов для более удобного при чтении форматирования получаемой строки. Важно помнить, что это третий параметр метода и если функция или массив не переданы вторым параметром - нужно указать вместо них null
.
const myObj = { name: "John", age: 22 };
console.log(JSON.stringify(myObj, null, 4));
// {
// "name": "John",
// "age": 22
// }
console.log(JSON.stringify(myObj, null, 10));
// {
// "name": "John",
// "age": 22
// }
//если не передавать null или второй параметр
//форматирование останется стандартным
console.log(JSON.stringify(myObj, 10));
//{"name":"John","age":22}
Мы уже знаем что при вызове JSON.stringify()
можно указать массив ключей или функцию, которая обработает все пары ключ-значение, но мы так же можем изменить поведение этого метода при описании объекта, который будет в него передаваться. Для этого нужно добавить такому объекту свойство toJSON и описать в нем функцию, возвращающую нужное нам значение. Таким образом можно “скрыть” для JSON.stringify()
приватные данные или вовсе передавать значения полностью отличные от содержимого объекта.
const user = {
name: "John",
age: 22,
password: "password",
toJSON() {
return { name: this.name, age: this.age };
},//перечисляем свойства которые хотим передавать
};
console.log(JSON.stringify(user, null, 2));
// {
// "name": "John",
// "age": 22
// }
Заключение
Работа с данными формата JSON и методами одноименного глобального объекта в JavaScript давно стала неотъемлемой частью разработки веб приложений поэтому навыки взаимодействия с ними необходимы каждому разработчику.
Комментарии
0