J q: jq Manual (development version)

fx — алтернатива jq для обработки JSON из командной строки / Хабр

jq — самая популярная утилита для обработки JSON из командной строки, написана на C и имеет свой собственный синтаксис для работы с JSON.

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

Так и появилась идея написать fx с простым и понятным синтаксисом, который никогда не забудешь. А какой язык программирования знают все? Правильно — JavaScript.

fx принимает в качестве аргумента код на JavaScript, содержащий анонимную функцию, и вызывает её, подставляя в качестве аргумента JSON, полученный из stdin. То, что функция вернёт, будет выведено в stdout. Все. Больше никаких сложных и непонятных конструкций.

Сравните два решения одной и той же задачи на jq и на fx:

jq '[.order[] as $x | .data[$x]]'
fx 'input => input.order.map(x => obj.data[x])'

Чуть более многословно? Да, это же просто обычный JavaScript.

Если fx вообще не передать аргументов, то JSON будет отформатирован и выведен:

$ echo '{"key":"value"}' | fx
{
    "key": "value"
}

Если код не содержит param =>, то переданный код будет автоматически преобразован в анонимную функцию и this будет содержать переданный JSON объект:

$ echo '{"foo": [{"bar": "value"}]}' | fx 'this.foo[0].bar'
"value"

fx можно передать несколько аргументов/анонимных функций, они будут применены поочерёдно к JSON:

$ echo '{"foo": [{"bar": "value"}]}' | fx 'x => x.foo' 'this[0]' 'this.bar'
"value"

Если код содержит ключевое слово yield, созданная анонимная функция будет содержать «развернутый» генератор (пример из generator-expression):

$ cat data.json | fx '\
for (let user of this) \ 
  if (user.login.startsWith("a")) \
    yield user'
$ echo '["a", "b"]' | fx 'yield* this'
[
    "a",
    "b"
]

Это позволяет описывать очень простые выборки в сложных запросах.

Кстати, модифицировать JSON с fx тоже очень просто:

$ echo '{"count": 0}' | fx '{...this, count: 1}'
{
    "count": 1
}

А также можно использовать любой npm пакет, если поставить его глобально:

$ npm install -g lodash
$ cat package.json | fx 'require("lodash").keys(this.dependencies)'

Для некоторых важно, что jq всего лишь один бинарник (~2mb). Так вот fx тоже имеет отдельные бинарники.

Весят они немного больше (~30mb), но если вам это не критично, и стоит nodejs, то поставить fx можно с помощью npm:

npm install -g fx

А что по производительности? Давайте замерим с помощью hyperfine:

$ curl 'https://api.github.com/repos/stedolan/jq/commits' > data.json
$ hyperfine --warmup 3 "jq ..." "fx ..."
Benchmark #1: cat data.json | jq '[.[] | {message: .commit.message, name: .commit.committer.name}]'
  Time (mean ± σ):      10.7 ms ±   0.9 ms    [User: 8.7 ms, System: 3.6 ms]
  Range (min … max):     9.0 ms …  14.9 ms
Benchmark #2: cat data.
json | fx 'this.map(({commit}) => ({message: commit.message, name: commit.committer.name}))' Time (mean ± σ): 159.6 ms ± 4.4 ms [User: 127.4 ms, System: 28.4 ms] Range (min … max): 153.0 ms … 170.0 ms

На порядок меньше. А все дело в том, что fx использует eval для запуска кода из аргументов (и вообще весь код fx <70 строк кода). Если для вас важна скорость обработки, не используйте fx. Во всех остальных случаях — fx отличный выбор.


  • fx
  • jq

Надеюсь, эта утилита кому-нибудь пригодится. Спасибо за внимание.

Электронные Сигареты JQ – Официальный сайт

В наличии

Стартовый Набор JQ Black

1200 i

Стартовый Набор JQ Black. Электронная Сигареты JQ со сменными картриджами Внешняя сторона настолько же важна, к…

В наличии

Стартовый Набор JQ Blue

1200 i

Стартовый Набор JQ Blue Электронная Сигареты JQ со сменными картриджами Внешняя сторона настолько же важна, как. ..

В наличии

Стартовый Набор JQ Gold

1200 i

Стартовый Набор JQ Gold Электронная Сигареты JQ со сменными картриджами Новые электронные испарители JQ голд в …

В наличии

Стартовый Набор JQ Red

1200 i

Стартовый Набор JQ Red Электронная Сигареты JQ со сменными картриджами Выделяться не стыдно. И если у вас в рук…

В наличии

Капсулы JQ Арбуз

349 i

Капсулы JQ со вкусом спелого Арбуза (2шт) Одними из наиболее популярных и востребованных линеек будут оставаться фру…

В наличии

Капсулы JQ Банан

349 i

Капсулы JQ со вкусом Банан (2шт) Мы не создаем новые тренды. Мы задаем им новые планки качества. Подтверждением этог…

В наличии

Капсулы JQ Виноград-мята

349 i

Капсулы JQ со вкусом Винограда и Мята (2шт) Рынок вкусов для электронных испарителен настолько большой, что сочетани. ..

В наличии

Капсулы JQ Вишня

349 i

Капсулы JQ со вкусом Вишни (2шт) Новые технологи, новый взгляд, новые фруктовые вкусы. Сменные капсулы JQ «Вишня» …

В наличии

Капсулы JQ Дыня

349 i

Капсулы JQ со вкусом Дыни (2шт) Мы используем другие формулы, которые отличаются от тех, которые вы привыкли видеть …

В наличии

Капсулы JQ Классик

350 i

Капсулы JQ (2шт) Мы хотим показать вам новый взгляд на то, какие вкусовые ощущение у вас могут вызывать электронные …

В наличии

Капсулы JQ Кофе

349 i

Капсулы JQ со вкусом Кофе (2шт) Энергичный заряд бодрости можно передать не только посредством чашки кофе. Спасибо 2…

В наличии

Капсулы JQ Лесные Ягоды

349 i

Капсулы JQ со вкусом Лесных Ягод (2шт) Эксперименты с фруктовыми миксами иногда дают плодотворные результаты. Именно…

В наличии

Капсулы JQ Манго-Айс

349 i

Капсулы JQ со вкусом Манго-Айс (2шт) Мы любим экзотику ровно также, как и вы. Эта любовь послужила мотивом для того,…

В наличии

Капсулы JQ Мята

349 i

Капсулы JQ со вкусом Освежающей Мяты (2шт) Существует категория покупателей, которые приобретают фруктовые и другие …

В наличии

Капсулы JQ Черная смородина-мята

349 i

Капсулы JQ со вкусом Черной смородины и Мяты (2шт) Фруктовые и ягодные вкусы хорошо сочетаются с мятой. Сегодня можн…

В наличии

Капсулы JQ Яблоко

349 i

Капсулы JQ со вкусом Яблоко (2шт) Не обязательно искать компромиссы, когда вы просто хотите увидеть в своей коллекци…

Скачать jq

jq написан на C и не имеет зависимостей во время выполнения, поэтому он должен быть можно построить его практически для любой платформы.

Готовые двоичные файлы доступно для Linux, OS X и Windows.

Двоичные файлы должны просто запускаться, но в OS X и Linux вам может понадобиться чтобы сначала сделать их исполняемыми, используя chmod +x jq .

jq находится под лицензией MIT. Для всей крови подробности читайте в файле COPYING в исходниках дистрибутива.

jq использует библиотеку C для поддержки десятичных чисел. Это реанимация 1.8.1 лицензионный код, полученный из архива загрузок ICU http://download.icu-project.org/files/decNumber/decNumber-icu-368.zip.

Линукс

  • jq 1.5 есть в официальном Debian и Репозитории Убунту. Установить с помощью sudo apt-get install jq .

  • jq 1.5 есть в официалке Репозиторий Федоры. Установите с помощью

    sudo dnf install jq .

  • jq 1.4 есть в официальном openSUSE репозиторий. Установите с помощью sudo zypper install jq .

  • jq 1.5 есть в официалке Арка репозиторий. Установить с помощью sudo pacman -S jq .

  • бинарных файла jq 1.6 для 64-битный или же 32-бит.

  • бинарных файла jq 1.5 для 64-битный или же 32-бит.

  • бинарных файла jq 1.4 для 64-битный или же 32-бит.

  • бинарных файла jq 1.3 для 64-битный или же 32-бит.

ОС Х

  • Используйте Homebrew для установки jq 1.6 с варить установить jq .

  • Используйте MacPorts для установки jq 1.6 с порт установить jq .

  • jq 1.6 бинарник для 64-бит.

  • jq 1.5 бинарный для 64-бит.

  • бинарных файла jq 1.4 для 64-битный или же 32-бит.

  • бинарных файла jq 1.3 для 64-битный или же 32-бит.

FreeBSD

Солярис

  • pkgutil -i jq в OpenCSW для Solaris 10+, Sparc и x86.

  • двоичных файла jq 1.4 для Solaris 11 64-битный или же 32-бит.

Окна

  • Используйте Chocolatey NuGet для установки jq 1.5 с шоколадный установить jq .

  • исполняемых файла jq 1.6 для 64-битный или же 32-бит.

  • исполняемых файла jq 1.5 для 64-битный или же 32-бит.

  • исполняемых файла jq 1.4 для 64-битный или же 32-бит.

  • исполняемых файла jq 1.3 для 64-битный или же 32-бит.

Контрольные суммы и подписи

Контрольные суммы SHA-256 предоставляются для всех выпускных и предварительных двоичных файлов. Их можно найти под sig/v1.x/sha256sum.txt. Контрольные суммы для jq 1.6 находятся в sig/v1.6/sha256sum.txt. Контрольные суммы для jq 1.5 находятся в sig/v1.5/sha256sum.txt.

Кроме того, все двоичные файлы подписаны Ключ подписи пакета jq. Подписи можно найти в разделе сиг/v1. x/*.asc. Подписи для jq 1.6 находятся в знак/v1.5/*.asc. Подписи для jq 1.5 находятся в знак/v1.5/*.asc. Вы можете использовать GnuPG для проверки подписи, загрузив подпись и бег gpg --проверить подпись.asc .

Из исходного кода для Linux, OS X, Cygwin и других POSIX-подобных операционных систем

  • Архив с исходным кодом для jq 1.6
  • Архив исходников для jq 1.5

Вы можете собрать его с помощью обычного ./configure && make && sudo make install вздор.

Если вы заинтересованы в использовании последней версии разработки, попробуйте:

 git clone https://github.com/stedolan/jq.git
компакт-диск JQ
автореконф -я
./configure --disable-maintainer-mode
делать
судо сделать установить
 

Чтобы собрать его из клона git, вам нужно установить несколько пакеты сначала:

  • GCC
  • Сделать
  • Автоинструменты

Для систем Linux все это будет в пакете вашей системы менеджер, и если вы делаете разработку на машине, они больше всего скорее всего уже установлен.

В OS X все они включены в инструменты командной строки Apple, которые могут быть установлен из Xcode. Однако, вы можете обнаружить, что вам нужна более новая версия Bison, чем предоставленная Apple. Это можно найти в Homebrew или MacPorts.

Флаг --disable-maintainer-mode говорит использовать предварительно сгенерированный лексер и парсер, который поставляется с кодом. Для компиляции лексера и парсера также из источника, оставьте этот флаг. Вам нужно будет установить Flex и Бизон.

Создание документации

Документация jq скомпилирована в статический HTML с помощью Python. Чтобы собрать документы, запустите pipenv, запустите python3 build_website.py из документы / подкаталог. Чтобы обслуживать их локально, вы можете запустить python3 -m SimpleHTTPServer . Вам понадобится несколько зависимостей Python, который можно установить, следуя инструкциям в docs/README.md .

Справочная страница создается с помощью make jq. 1 или просто make , также из документы YAML, и вам все равно понадобятся зависимости Python для построить справочную страницу.

Tutorial

У GitHub есть JSON API, так что давайте поэкспериментируем с ним. Этот URL дает нам последний 5 коммитов из репозитория jq.

 завиток 'https://api.github.com/repos/stedolan/jq/commits?per_page=5' 

Показать результат

 [
  {
    "ша": "d25341478381063d1c76e81b3a52e0592a7c997f",
    "совершить": {
      "автор": {
        "name": "Стивен Долан",
        "электронная почта": "[email protected]",
        "дата": "2013-06-22T16:30:59Z"
      },
      "коммиттер": {
        "name": "Стивен Долан",
        "электронная почта": "[email protected]",
        "дата": "2013-06-22T16:30:59Z"
      },
      "message": "Запрос на слияние #162 из stedolan/utf8-fixes\n\nИсправления Utf8. Закрывается #161",
      "дерево": {
        "ша": "6ab697a8dfb5a96e124666bf6d6213822599fb40",
        "url": "https://api. github.com/repos/stedolan/jq/git/trees/6ab697a8dfb5a96e124666bf6d6213822599fb40"
      },
      "url": "https://api.github.com/repos/stedolan/jq/git/commits/d25341478381063d1c76e81b3a52e0592a7c997f",
      "количество_комментариев": 0
    },
    "url": "https://api.github.com/repos/stedolan/jq/commits/d25341478381063d1c76e81b3a52e0592a7c997f",
    "html_url": "https://github.com/stedolan/jq/commit/d25341478381063d1c76e81b3a52e0592a7c997f",
    "comments_url": "https://api.github.com/repos/stedolan/jq/commits/d25341478381063d1c76e81b3a52e0592a7c997f/комментарии",
    "автор": {
      "логин": "стедолан",
...
 

GitHub возвращает красиво отформатированный JSON. Для серверов, которые этого не делают, это может быть полезно передать ответ через jq, чтобы распечатать его красиво. Простейший jq — это выражение . , который принимает ввод и производит его без изменений в качестве вывода.

 curl 'https://api.github.com/repos/stedolan/jq/commits?per_page=5' | jq '. ' 

Показать результат

 [
  {
    "ша": "d25341478381063d1c76e81b3a52e0592a7c997f",
    "совершить": {
      "автор": {
        "name": "Стивен Долан",
        "электронная почта": "[email protected]",
        "дата": "2013-06-22T16:30:59Z"
      },
      "коммиттер": {
        "name": "Стивен Долан",
        "электронная почта": "[email protected]",
        "дата": "2013-06-22T16:30:59Z"
      },
      "message": "Запрос на слияние #162 из stedolan/utf8-fixes\n\nИсправления Utf8. Закрывается #161",
      "дерево": {
        "ша": "6ab697a8dfb5a96e124666bf6d6213822599fb40",
        "url": "https://api.github.com/repos/stedolan/jq/git/trees/6ab697a8dfb5a96e124666bf6d6213822599fb40"
      },
      "url": "https://api.github.com/repos/stedolan/jq/git/commits/d25341478381063d1c76e81b3a52e0592a7c997f",
      "количество_комментариев": 0
    },
    "url": "https://api.github.com/repos/stedolan/jq/commits/d25341478381063d1c76e81b3a52e0592a7c997f",
    "html_url": "https://github. com/stedolan/jq/commit/d25341478381063d1c76e81b3a52e0592a7c997f",
    "comments_url": "https://api.github.com/repos/stedolan/jq/commits/d25341478381063d1c76e81b3a52e0592a7c997f/comments",
    "автор": {
      "логин": "стедолан",
...
 

Мы можем использовать jq для извлечения только первого коммита.

 curl 'https://api.github.com/repos/stedolan/jq/commits?per_page=5' | jq '.[0]' 

Показать результат

 {
  "ша": "d25341478381063d1c76e81b3a52e0592a7c997f",
  "совершить": {
    "автор": {
      "name": "Стивен Долан",
      "электронная почта": "[email protected]",
      "дата": "2013-06-22T16:30:59Z"
    },
    "коммиттер": {
      "name": "Стивен Долан",
      "электронная почта": "[email protected]",
      "дата": "2013-06-22T16:30:59З"
    },
    "message": "Запрос на слияние #162 из stedolan/utf8-fixes\n\nИсправления Utf8. Закрывается #161",
    "дерево": {
      "ша": "6ab697a8dfb5a96e124666bf6d6213822599fb40",
      "url": "https://api. github.com/repos/stedolan/jq/git/trees/6ab697a8dfb5a96e124666bf6d6213822599fb40"
    },
    "url": "https://api.github.com/repos/stedolan/jq/git/commits/d25341478381063d1c76e81b3a52e0592a7c997f",
    "количество_комментариев": 0
  },
  "url": "https://api.github.com/repos/stedolan/jq/commits/d25341478381063d1c76e81b3a52e0592a7c997f",
  "html_url": "https://github.com/stedolan/jq/commit/d25341478381063d1c76e81b3a52e0592a7c997f",
  "comments_url": "https://api.github.com/repos/stedolan/jq/commits/d25341478381063d1c76e81b3a52e0592a7c997f/comments",
  "автор": {
    "логин": "стедолан",
    "идентификатор": 79765,
    "avatar_url": "https://avatars.githubusercontent.com/u/79765?v=3",
    "gravatar_id": "",
    "url": "https://api.github.com/users/stedolan",
    "html_url": "https://github.com/stedolan",
    "followers_url": "https://api.github.com/users/stedolan/followers",
    "following_url": "https://api.github.com/users/stedolan/following{/other_user}",
    "gists_url": "https://api.github. com/users/stedolan/gists{/gist_id}",
    "starred_url": "https://api.github.com/users/stedolan/starred{/owner}{/repo}",
    "subscriptions_url": "https://api.github.com/users/stedolan/subscriptions",
    "organizations_url": "https://api.github.com/users/stedolan/orgs",
    "repos_url": "https://api.github.com/users/stedolan/repos",
    "events_url": "https://api.github.com/users/stedolan/events{/privacy}",
    "received_events_url": "https://api.github.com/users/stedolan/received_events",
    "тип": "Пользователь",
    "сайт_админ": ложь
  },
  "коммиттер": {
    "логин": "стедолан",
    "идентификатор": 79765,
    "avatar_url": "https://avatars.githubusercontent.com/u/79765?v=3",
    "gravatar_id": "",
    "url": "https://api.github.com/users/stedolan",
    "html_url": "https://github.com/stedolan",
    "followers_url": "https://api.github.com/users/stedolan/followers",
    "following_url": "https://api.github.com/users/stedolan/following{/other_user}",
    "gists_url": "https://api.github. com/users/stedolan/gists{/gist_id}",
    "starred_url": "https://api.github.com/users/stedolan/starred{/owner}{/repo}",
    "subscriptions_url": "https://api.github.com/users/stedolan/subscriptions",
    "organizations_url": "https://api.github.com/users/stedolan/orgs",
    "repos_url": "https://api.github.com/users/stedolan/repos",
    "events_url": "https://api.github.com/users/stedolan/events{/privacy}",
    "received_events_url": "https://api.github.com/users/stedolan/received_events",
    "тип": "Пользователь",
    "сайт_админ": ложь
  },
  "родители": [
    {
      "ша": "54b9c9bdb225af5d886466d72f47eafc51acb4f7",
      "url": "https://api.github.com/repos/stedolan/jq/commits/54b9c9bdb225af5d886466d72f47eafc51acb4f7",
      "html_url": "https://github.com/stedolan/jq/commit/54b9c9bdb225af5d886466d72f47eafc51acb4f7"
    },
    {
      "ша": "8b1b503609c161fea4b003a7179b3fbb2dd4345a",
      "url": "https://api.github.com/repos/stedolan/jq/commits/8b1b503609c161fea4b003a7179b3fbb2dd4345a",
      "html_url": "https://github. com/stedolan/jq/commit/8b1b503609c161fea4b003a7179b3fbb2dd4345a"
    }
  ]
}
 

В остальных примерах я не буду использовать команду curl — это не так. собирается измениться.

Там много информации, которая нам не нужна, поэтому мы ограничим ее в самые интересные области.

 jq '.[0] | {сообщение: .commit.message, имя: .commit.committer.name}' 

Показать результат

 {
  "message": "Запрос на слияние #162 из stedolan/utf8-fixes\n\nИсправления Utf8. Закрывается #161",
  "name": "Стивен Долан"
}
 

| Оператор в jq передает вывод одного фильтра ( .[0] , который получает первый элемент массива в ответе) на вход другого ( {...} , который создает объект из этих полей). Вы можете получить доступ вложенные атрибуты, такие как .commit.message .

Теперь займемся остальными коммитами.

 jq '.[] | {сообщение: .commit.message, имя: . commit.committer.name}' 

Показать результат

 {
  "message": "Запрос на слияние #162 из stedolan/utf8-fixes\n\nИсправления Utf8. Закрывается #161",
  "name": "Стивен Долан"
}
{
  "message": "Отклонить все чрезмерно длинные последовательности UTF8.",
  "name": "Стивен Долан"
}
{
  "message": "Исправить различные ошибки синтаксического анализа UTF8.\n\nВ частности, разобрать неверный код UTF8, заменив битые биты на U+FFFD\nи правильно выполнить ресинхронизацию после поврежденных последовательностей.",
  "name": "Стивен Долан"
}
{
  "message": "Исправить пример в руководстве для `floor`. См. #155.",
  "name": "Стивен Долан"
}
{
  "message": "Документ пол",
  "name": "Николас Уильямс"
}
 

.[] возвращает каждый элемент массива, возвращаемый в ответе, по одному за время, которое все подается в {сообщение: .commit.message, имя: .commit.committer.name} .

Данные в jq представлены в виде потоков значений JSON — каждый jq выражение выполняется для каждого значения в его входном потоке и может производить любое количество значений в свой выходной поток.

Потоки сериализуются путем простого разделения значений JSON с помощью пробел. Это дружественный формат cat — вы можете просто присоединиться два потока JSON вместе и получить действительный поток JSON.

Если вы хотите получить вывод в виде единого массива, вы можете указать jq «соберите» все ответы, обернув фильтр квадратом скобки:

 jq '[.[] | {сообщение: .commit.message, имя: .commit.committer.name}]' 

Показать результат

 [
  {
    "message": "Запрос на слияние #163 из stedolan/utf8-fixes\n\nИсправления Utf8. Закрывается #161",
    "name": "Стивен Долан"
  },
  {
    "message": "Отклонить все чрезмерно длинные последовательности UTF8.",
    "name": "Стивен Долан"
  },
  {
    "message": "Исправить различные ошибки синтаксического анализа UTF8.\n\nВ частности, разобрать неверный код UTF8, заменив битые биты на U+FFFD\nи правильно выполнить ресинхронизацию после поврежденных последовательностей. ",
    "name": "Стивен Долан"
  },
  {
    "message": "Исправить пример в руководстве для `floor`. См. #155.",
    "name": "Стивен Долан"
  },
  {
    "message": "Этаж документов",
    "name": "Николас Уильямс"
  }
]
 

Теперь попробуем получить URL-адреса родительских коммитов из А также результаты API. В каждом коммите GitHub API включает информацию о «родительских» коммитах. Может быть один или много.

 "родители": [
  {
    "ша": "54b9c9bdb225af5d886466d72f47eafc51acb4f7",
    "url": "https://api.github.com/repos/stedolan/jq/commits/54b9c9bdb225af5d886466d72f47eafc51acb4f7",
    "html_url": "https://github.com/stedolan/jq/commit/54b9c9bdb225af5d886466d72f47eafc51acb4f7"
  },
  {
    "ша": "8b1b503609c161fea4b003a7179b3fbb2dd4345a",
    "url": "https://api.github.com/repos/stedolan/jq/commits/8b1b503609c161fea4b003a7179b3fbb2dd4345a",
    "html_url": "https://github.com/stedolan/jq/commit/8b1b503609c161fea4b003a7179b3fbb2dd4345a"
  }
]
 

Мы хотим извлечь все поля «html_url» внутри этого массива родительского коммитов и составить простой список строк, которые будут сопровождать Поля «сообщение» и «автор» у нас уже есть.

 jq '[.[] | {сообщение: .commit.message, имя: .commit.committer.name, родители: [.parents[].html_url]}]' 

Показать результат

 [
 {
 "message": "Запрос на слияние #162 из stedolan/utf8-fixes\n\nИсправления Utf8. Закрывается #161",
 "name": "Стивен Долан",
 "родители": [
 "https://github.com/stedolan/jq/commit/54b9c9bdb225af5d886466d72f47eafc51acb4f7",
 "https://github.com/stedolan/jq/commit/8b1b503609c161fea4b003a7179b3fbb2dd4345a"
 ]
 },
 {
 "message": "Отклонить все чрезмерно длинные последовательности UTF8.",
 "name": "Стивен Долан",
 "родители": [
 "https://github.com/stedolan/jq/commit/ff48bd6ec538b01d1057be8e93b94eef6914e9ef"
 ]
 },
 {
 "message": "Исправить различные ошибки синтаксического анализа UTF8.\n\nВ частности, разобрать неверный код UTF8, заменив битые биты на U+FFFD\nи правильно выполнить ресинхронизацию после поврежденных последовательностей.",
 "name": "Стивен Долан",
 "родители": [
 "https://github.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *