JavaScript с нуля — страница 18 из 30

Глава 23. Все о JSON (объектная нотация JavaScript)

Когда дело доходит до хранения, извлечения или передачи данных, то в нашем распоряжении оказывается множество форматов файлов и структур данных. Вы наверняка уже использовали текстовые файлы, документы Word, электронные таблицы Excel и т. д. Что же касается фронтенд-разработки, то здесь лидирует один формат — JSON, JavaScript Object Notation.

В этой главе мы узнаем все о том, что делает объекты JSON столь прекрасными, рассмотрим в деталях происходящее внутри них и узнаем, как можно считывать их значения для ваших собственных реализаций.

Поехали!

Что такое JSON?

В JavaScript для определения объектов вы используете специальный синтаксис объектного литерала:

let funnyGuy = {

firstName: "Conan",

lastName: "O'Brien",


getName: function () {

return "Name is: " + this.firstName + " " + this.lastName;

}

};


let theDude = {

firstName: "Jeffrey",

lastName: "Lebowski",


getName: function () {

return "Name is: " + this.firstName + " " + this.lastName;

}

};


let detective = {

firstName: "Adrian",

lastName: "Monk",


getName: function () {

return "Name is: " + this.firstName + " " + this.lastName;

}

};

Если вы не понимаете этот синтаксис, то настоятельно рекомендую почитать раздел «Об объектах подробней». Это существенно упростит понимание объектов JSON и работу с ними.

Внешне синтаксис объектного литерала выглядит как куча скобок, фигурных скобок и двоеточий, которые определяют свойства и значения объекта. Несмотря на такую внешнюю странность, изнутри все достаточно наглядно. Вы вольны использовать большинство из распространенных типов данных и обстоятельно представлять их свойства и значения в виде пар ключей и значений, разделенных двоеточием. Помимо всего перечисленного не менее важно то, что этот синтаксис также позволяет создавать структуру и допускает вложенные значения. В целом это достаточно приятный способ представления объектов JavaScript в литеральном виде.

Формат JSON очень многое заимствует от синтаксиса объектного литерала. Ниже приведен пример реальных данных JSON, возвращенных API WeatherUnderground для отображения погоды в моем родном городе Сиэтле:

{

"response": {

"version": "0.1",

"termsofService":

"http://www.wunderground.com/weather/api/d/terms.html",

"features": {

"conditions": 1

}

},

"current_observation": {

"image": {

"url": "http://icons.wxug.com/graphics/wu2/logo_130x80.png",

"title": "Weather Underground",

"link": "http://www.wunderground.com"

},

"display_location": {

"full": "Seattle, WA",

"city": "Seattle",

"state": "WA",

"state_name": "Washington",

"country": "US",

"country_iso3166": "US",

"zip": "98101",

"magic": "1",

"wmo": "99999",

"latitude": "47.61167908",

"longitude": "-122.33325958",

"elevation": "63.00000000"

},


"observation_location": {

"full": "Herrera, Inc., Seattle, Washington",

"city": "Herrera, Inc., Seattle",

"state": "Washington",

"country": "US",

"country_iso3166": "US",

"latitude": "47.616558",

"longitude": "-122.341240",

"elevation": "121 ft"

},

"estimated": {},

"station_id": "KWASEATT187",

"observation_time": "Last Updated on August 28, 9:28 PM PDT",

"observation_time_rfc822": "Fri, 28 Aug 2015 21:28:12 -0700",

"observation_epoch": "1440822492",

"local_time_rfc822": "Fri, 28 Aug 2015 21:28:45 -0700",

"local_epoch": "1440822525",

"local_tz_short": "PDT",

"local_tz_long": "America/Los_Angeles",

"local_tz_offset": "-0700",

"weather": "Overcast",

"temperature_string": "68.0 F (20.0 C)",

"temp_f": 68.0,

"temp_c": 20.0,

"relative_humidity": "71 %",

"wind_string": "Calm",

"wind_dir": "NNW",

"wind_degrees": 331,

"wind_mph": 0.0,

"wind_gust_mph": "10.0",

"wind_kph": 0,

"wind_gust_kph": "16.1",

"pressure_mb": "1008",

"pressure_in": "29.78",

"pressure_trend": "-",

"dewpoint_string": "58 F (15 C)",

"dewpoint_f": 58,

"dewpoint_c": 15,

"heat_index_string": "NA",

"heat_index_f": "NA",

"heat_index_c": "NA",

"windchill_string": "NA",

"windchill_f": "NA",

"windchill_c": "NA",

"feelslike_string": "68.0 F (20.0 C)",

"feelslike_f": "68.0",

"feelslike_c": "20.0",

"visibility_mi": "10.0",

"visibility_km": "16.1",

"solarradiation": "-",

"UV": "0",

"precip_1hr_string": "0.00 in (0 mm)",

"precip_1hr_in": "0.00",

"precip_1hr_metric": " 0",

"precip_today_string": "0.00 in (0 mm)",

"precip_today_in": "0.00",

"precip_today_metric": "0",

"icon": "cloudy",

"icon_url": "http://icons.wxug.com/i/c/k/nt_cloudy.gif",

"nowcast": ""

}

}

Если не обращать внимания на размер, то данные JSON, которые вы видите, имеют много схожего с синтаксисом объектного литерала, виденным вами ранее. Также нужно знать и о некоторых существенных их различиях, но эту занудную тему мы затронем несколько позже. Сначала давайте посмотрим, из чего именно состоит объект JSON.

Объект JSON изнутри

Объект JSON — не более чем комбинация имен свойств и их значений. Звучит очень просто, но есть в этом важные детали, которые мы рассмотрим в текущем разделе.

Имена свойств

Имена свойств являются идентификаторами, которые вы будете использовать для обращения к значению. Располагаются они слева от двоеточия:

{

"firstName": "Kirupa",

"lastName": "Chinnathambi",

"special": {

"admin": true,

"userID": 203

},

"devices": [

{

"type": "laptop",

"model": "Macbook Pro 2015"

},

{

"type": "phone",

"model": "iPhone 6"

}

]

}

В этом фрагменте JSON имена свойств — это firstName, lastName, special, admin, userID, devices, type и model. Обратите внимание на то, как они определены, то есть представлены в виде строчных значений, заключенных в кавычки. Кавычки — это важная деталь, которую вам не требуется использовать для имен свойств в случае с объектными литералами. Поэтому имейте их в виду, когда будете работать в мире JSON.

Значения

Каждое имя свойства отображается в значение. Сами же значения могут иметь следующие типы:

• числа;

• строки;

• логические типы (true или false);

• объекты;

• массивы;

• Null.

Давайте сопоставим эти типы с примером, который видели недавно.

Строки

Ниже выделены именно строчные значения:

{

"firstName": "Kirupa",

"lastName": "Chinnathambi",

"special": {

"admin": true,

"userID": 203

},

"devices": [

{

"type": "laptop",

"model": "Macbook Pro"

{

},

"type": "phone",

"model": "iPhone XS"

}

]

}

Двойные кавычки являются убедительным признаком того, что представленные значения — это строки. Помимо привычных букв, чисел и символов можно включать в них экранирующие символы вроде \', \", \\, \/ и т. д., чтобы определять те знаки строки, которые в противном случае будут считаны как операция JSON.

Числа

В нашем примере представлен единственный член семейства чисел — это значение свойства userID:

{

"firstName": "Kirupa",

"lastName": "Chinnathambi",

"special": {

"admin": true,

"userID": 203

},

"devices": [

{

"type": "laptop",

"model": "Macbook Pro"

},

{

"type": "phone",

"model": "iPhone XS"

}

]

}

Вы можете указывать как десятичные значения (например 0.204, 1200.23, 45), так и экспоненциальные (2e16, 3e+4, 1.5e-2). При этом нужно помнить о том, что нельзя использовать префикс, начинающийся с нуля, сопровождаемого числом. Например, значение 03.14 недопустимо.

Логический тип

Логические значения весьма просты:

{

"firstName": "Kirupa",

"lastName": "Chinnathambi",

"special": {

"admin": true,

"userID": 203

},

"devices": [


{

"type": "laptop",

"model": "Macbook Pro"

},

{

"type": "phone",

"model": "iPhone XS"

}

]

}

Эти значения могут быть либо true, либо false. Здесь важно помнить, что регистр важен. Оба упомянутых значения должны использоваться в нижнем регистре. Печать в режиме предложения (True или False) либо использование только верхнего регистра (TRUE или FALSE) запрещено.

Объекты

А вот тут ситуация немного интереснее:

{

"firstName": "Kirupa",

"lastName": "Chinnathambi",

"special": {

"admin": true,

"userID": 203

},

"devices": [

{

"type": "laptop",

"model": "Macbook Pro"

},

{

"type": "phone",

"model": "iPhone XS"

}

]

}

Объекты содержат коллекцию имен свойств и значений, отделяясь от остального содержимого фигурными скобками. Видите? Разве это не было немного интереснее?

Массивы

Наше свойство devices представляет массив:

{

"firstName": "Kirupa",

"lastName": "Chinnathambi",

"special": {

"admin": true,

"userID": 203

},

"devices": [

{

"type": "laptop",

"model": "Macbook Pro"

},

{

"type": "phone",

"model": "iPhone XS"

}

]

}

Массивы хранят упорядоченную коллекцию значений, которые вы можете итерировать. Отделяются они скобочной нотацией. Внутри массива допускается использование любого типа JSON, который мы встречали до сих пор, включая и другие массивы.

Null

Последний тип данных самый унылый:

{

"foo": null

}

Ваши значения JSON могут быть null, что означает пустое значение.

Чтение данных JSON

Признаю, что предыдущий раздел был занудный, но есть и хорошие новости! На фоне ужасной скукоты предыдущего раздела этот покажется куда более захватывающим, чем есть на самом деле.

Как бы то ни было, практически всегда ваше взаимодействие с JSON будет связано с чтением данных. Когда дело доходит до чтения данных JSON, главное помнить, что это очень похоже на чтение значений, хранящихся в типичном объекте JavaScript. Вы можете либо обратиться к необходимому значению через точку (property.propertyFoo), либо использовать для этого подход массива (property["propertyFoo"]).

Следующий пример продемонстрирует это:

let exampleJSON = {

"firstName": "Kirupa",

"lastName": "Chinnathambi",

"special": {

"admin": true,

"userID": 203

},

"devices": [

{

"type": "laptop",

"model": "Macbook Pro"

},

{

"type": "phone",

"model": "iPhone XS"

}

]

};

Чтобы считать значение, хранящееся в firstName, вы можете сделать одно из следующего:

exampleJSON.firstName;

exampleJSON["firstName"];

Обе строки вернут значение Kirupa. При этом нет принципиальной разницы, получите ли вы нужное значение посредством точечной нотации или с помощью массива. Можете выбирать тот способ, который будет более удобен, но лично я предпочитаю именно точечную нотацию. От передачи имен свойств в виде строк меня немного мутит, поэтому во всех будущих фрагментах кода я буду выделять именно точечную нотацию.

Аналогично тому, что вы видели ранее, для обращения к значению, хранящемуся в lastName, вы можете сделать следующее:

exampleJSON.lastName;

В случае простых свойств, хранящих простые значения, все достаточно просто. Есть, правда, очень и очень маленькая трудность, с которой вы можете столкнуться, и связана она с более сложными значениями, состоящими из объектов и массивов. Чтобы считать значение, содержащееся в объекте, просто продолжайте через точку обращаться к каждому свойству, пока не достигните того, которое и хранит нужное вам значение.

Вот пример того, как выглядит попытка обращения к значению, содержащемуся в свойстве userID:

exampleJSON.special.userID;

Массивы ничем не отличаются, но вам придется переключиться на нотацию массива, как только вы достигните свойства, которое хранит значения массива. Если бы мы хотели обратиться к значению model первого устройства в массиве devices, то могли бы написать, например, следующее:

exampleJSON.devices[0].model;

Так как свойство devices относится к массиву, вы также можете производить стандартные присущие массивам операции вроде такой:

let devicesArray = exampleJSON.devices;


for (let i = 0; i < devicesArray.length; i++) {

let type = devicesArray[i].type;

let model = devicesArray[i].model;


// Делает что-нибудь интересное с этими данными.

}

Напоминая пройденное в предыдущем разделе, скажу, что ваши значения JSON могут быть строками, числами, объектами, массивами, логическими типами или null. Все, что JavaScript поддерживает для заданного типа данных, встреченного вами в объекте JSON, вы можете использовать в своих интересах.

Парсинг JSON-подобных данных в действительный JSON

В нашем примере данные JSON были точно определены внутри переменной exampleJSON. Ни у кого не возникнет сомнений, что мы имеем дело именно с реальным объектом JS, представленным посредством семантики JSON.

В реальной жизни подобные сценарии будут не всегда. Ваши данные JSON могут поступать из множества различных ресурсов, не все из которых будут возвращать их в удобном для работы формате, аналогичном тому, что мы только что видели. Многие будут возвращать данные JSON в виде сырого текста. В итоге у вас будет что-то похожее на объект JSON, но вы не сможете взаимодействовать с данными так, как это возможно при работе с реальным объектом JSON.

Для таких случаев существует метод JSON.parse, который получает ваши «недействительные» данные JSON в виде аргумента:

function processRequest(e) {

if (xhr.readyState == 4 && xhr.status == 200) {

let response = JSON.parse(xhr.responseText);

selectInitialState(response.region);

}

}

Как видно по выделенной строке, этот метод получает любые JSON-подобные конечные данные и преобразует их в реальный объект JSON, с которым уже гораздо легче работать. Лично я при работе с внешними JSON-данными всегда использую JSON.parse просто для безопасности.

Запись данных JSON?

Текущий раздел был целиком посвящен чтению значений из данных JSON. Было бы логичным посвятить аналогичный раздел их записи. Но как выясняется, запись данных JSON вовсе не так популярна, если только вы не сохраняете их в файл или не работаете с веб-службами. Если вы выполняете какую-либо из этих задач, то очевидно, что это либо разработка на Node, либо написание кода на не JavaScript.

В случае же с фронтенд-разработкой я не представляю распространенных случаев, где запись данных JSON была бы актуальна. Если вы столкнетесь с какой-либо редкой ситуацией, в которой ваши потребности не ограничатся только чтением данных JSON, то гугл в помощь!

КОРОТКО О ГЛАВНОМ

Раньше эта глава была бы посвящена XML. Даже сегодня XML по-прежнему широко используется как формат файлов для хранения информации и ее обмена. JSON же чрезвычайно популярен только в том мире, где браузер является королем (то есть в мире, где живем мы с вами). Вне веб-сайтов, веб-приложений и веб-сервисов, основанных на REST, работа с данными в формате JSON не приветствуется. Вам следует иметь это в виду, если вы столкнетесь с более старыми, менее веб-ориентированными ситуациями.

Если у вас появились какие-либо связанные с JSON или чем-то другим вопросы, отправляйтесь за ответами на форум https://forum.kirupa.com.

Глава 24. JS, браузер и DOM