JSON

В главе Внешние API уже описано как начать работать с JSON. Так вот пора оседлать эту лошадку 🐎!

Иногда в одной строке удобно хранить несколько переменных. Как в сумочке может уместиться одновременно множество вещей.

Допустим, 7 августа 2018 года в 22:36 Мария Ивановна решила признаться в любви юноше по имени JSON.

Время необходимо перевести в формат UNIX. Это делается для того, чтобы было удобно проводить вычисления, например, отнимать одну дату от другой. Здесь можно перевести время в формат UNIX и обратно. В нашем примере 7 августа 2018 года 22:36 будет ни что иное как 1533670582.

Запишем этот факт в переменную %X в виде JSON строки:

%X={"Name":"Мария Ивановна","Message":"I ❤️ JSON","Time":1533670582}

Если расшифровать содержание переменной X, окажется, что:

Name="Мария Ивановна"

Message="I ❤️ JSON"

Time=1533675776

Отформатированная строка JSON выглядит так:

{
"Name": "Мария Ивановна",
"Message": "I ❤️ JSON",
"Time": 1533670582
}

🐫 Обратите внимание, что тексты берутся в кавычки, а числа нет.

Вроде понятно! Осталось научить этому бота.

Разберём как выглядит узел для работы с JSON:

$.jsonPath %variable method(),

где jsonPath — путь к переменной внутри JSON (можно опустить при работе с корневым элементом);

%variable — переменная содержащая строку JSON;

method() — имя метода (не используется в режиме чтения).

Чтение переменной из строки JSON

Как мы помним в переменной X лежит строка JSON {"Name":"Мария Ивановна","Message":"I ❤️ JSON","Time":1533670582} или:

{
"Name": "Мария Ивановна",
"Message": "I ❤️ JSON",
"Time": 1533670582
}

Прочтем Message из нашей JSON-строки:

%Y=$.Message %X

Теперь %Y содержит сообщение “I ❤️ JSON”.

Ура!

А сейчас немного усложним задачу. Представим, что наша Мария Ивановна, которой 31 год, решила пойти в гастроном. Для этого она написала список продуктов, разделив их по группам: фрукты, овощи, мясо.

{
"name": "Мария Ивановна",
"age": 31,
"gender": "Female",
"grocery": [
{
"name": "Fruits",
"variety": [
"Orange",
"Apple",
"Strawberry"
]
},
{
"name": "Vegetables",
"variety": [
"Avocado",
"Tomato",
"Chilli"
]
},
{
"name": "Meat",
"variety": [
"Pork",
"Beef",
"Chicken"
]
}
]
}

Попробуем узнать что у Марии Ивановны первое в списке продуктов из мясного отдела. Для этого нам нужно указать путь к месту в JSON-строке с нужной переменной.

Этот путь выглядит так: grocery[2].variety[0].

А это весь узел, который положит значение в нужную переменную %Y:

%Y=$.grocery[2].variety[0] %X

Как же узнать путь до нужного значения? В нашем случае JSON содержит переменные на нескольких уровнях. На первом уровне name, age, gender и grocery. Нас интересует переменная grocery, ведь в ней содержится список покупок. Идем в мясную категорию. Хотя в списке она третья, но имеет номер 2, потому что нумерация в JSON начинается с нуля, а не с единицы. Таким образом grocery[2] означает, что мы находимся в мясном отделе. Остается выбрать первый продукт в списке. Как мы теперь знаем, его порядковый номер 0, соответственно нам нужна variety[0]. Таким образом путь grocery[2].variety[0] укажет нам на нужное значение, а узел %Y=$.grocery[2].variety[0] %X положит в %Y значение Pork.

Вот и всё!

🐫 Проверить путь к нужной переменной JSON можно тут http://jsonpath.com

А вдруг мы понятия не имеем на каком месте в списке Марии Ивановны находятся мясные продукты? Тогда просто найдем их по имени

%Y=$.grocery[?(@.name=="Meat")].variety[0] %X

Для этого в пути укажем имя раздела Meat и возьмем первое значение. Получится свинина! 🐷

Мало того мы можем получать целые куски JSON. Например, если нам нужен перечень всех мясных продуктов из списка Марии Ивановны, сделаем это так:

%Y=$.grocery[?(@.name=="Meat")]

Таким образом, %Y тоже станет JSON-строкой со следующим содержанием:

{
"name": "Meat",
"variety": [
"Pork",
"Beef",
"Chicken"
]
}

Добавление данных в JSON

Как добавить данные в существующий JSON? Наш JSON находится в переменной %X. Добавим в список Марии Ивановны сыр. Для этого запишем в %Z перечень сыров:

%Z={"name":"Cheese","variety":["Cheddar","Mozzarella","Feta"]}

А теперь добавим все это в %X:

$.grocery %X add(%Z)

Получим такой вот JSON:

{
"name": "Мария Ивановна",
"age": 31,
"gender": "Female",
"grocery": [
{
"name": "Fruits",
"variety": [
"Orange",
"Apple",
"Strawberry"
]
},
{
"name": "Vegetables",
"variety": [
"Avocado",
"Tomato",
"Chilli"
]
},
{
"name": "Meat",
"variety": [
"Pork",
"Beef",
"Chicken"
]
},
{
"name": "Cheese",
"variety": [
"Cheddar",
"Mozzarella",
"Feta"
]
}
]
}

Удаление по значению поля

Давайте удалим мясо из списка покупок:

$.grocery %X remove({"name":"Meat"})

Также можно удалить данные с помощью jsonPath

$.grocery[?(@.name=="Meat")] %X remove()

Теперь Мария Ивановна еще и вегетарианка.

Достать последний элемент JSON

Например последний добавленный сыр:

%Y=$.grocery[?(@.name=="Cheese")].variety %X pop()

%Y будет содержать Feta. В тоже время из JSON этот элемент будет удален.

Итерирование

Теперь самая интересная штука.

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

$.grocery[?(@.name=="Fruits")].variety %X each(%item)

Берем JSON %X, указываем путь к фруктам $.grocery[?(@.name=="Fruits")].variety и кладем по очереди фрукты в %item.

Осталось из этого узла сделать две ветки:

  1. @next — что делать с элементом %item. В нашем примере он просто выводится в сообщении.
  2. @exit — что делать, когда элементы закончатся: идем дальше по карте.

Заработало!

Примеры последовательно показаны в карте:

Click icon to copy this map or open it in