Вопросы о выводе результатов в JSON являются довольно частыми среди всех обращений в Техническую поддержку. Поэтому мы подготовили небольшой обзор всех возможных способов это сделать.
В качестве "подопытного" возьмем парсер Google подсказок
SE::Google::Suggest. Также для большей наглядности условимся что в результате нам необходимы сам запрос, кол-во подсказок по нему, непосредственно сами подсказки и количество символов в каждой подсказке (ее длина).
1. Вывод в JSON для одиночных запросов
Простейший вариант вывода в JSON - это использование метода шаблонизатора .json:
Здесь мы в Общем формате результата создаем пустой объект obj, записываем в него две переменные (запрос query и кол-во результатов по нему count) и пустой массив suggests. После чего наполняем массив объектами, содержащими подсказку keyword и кол-во символов в ней length. Тем самым получается нужная структура. И выводим это все в виде JSON строки:
Этот способ отлично подходит для одиночных запросов, например через API метод oneRequest.
2. Формирование JSON построчно
Но что будет, если в предыдущем случае подать на вход несколько запросов? Проверим, добавив для удобства перевод строки после вывода JSON. И попробуем поработать с полученным JSON'ом, вставив его в любой редактор JSON:
Как видно на скриншоте, мы получили ошибку, т.к. JSON не валиден. Сейчас это по сути набор строк, который не отвечает правилам JSON.
Самым очевидным решением по формированию валидного JSON'а для нескольких запросов (да и в целом для любого количества больше одного) является использование массива. Если посмотреть на любой массив в JSON, то он похож на строки, разделенные запятыми. И все это взято в квадратные скобки. Что ж, добавим к выводу запятые (вместе с переводом строки, в формате результата) и квадратные скобки в самом начале и конце (воспользуемся для этого функционалом Начального и Конечного текстов).
Код шаблона в Формате результата по сути остается такой же, как и предыдущем примере, только добавляется запятая после каждой строки (и перевод строки для удобства):
Начальный и Конечный тексты:
Получаем такой результат:
Как видим, снова ошибка
Если изучить то, что получилось - становится понятно, что все портит запятая в самом конце (на скрине выделена стрелкой). Но она выводится вместе со строкой JSON'а и нет возможности указать парсеру что какая-то строка последняя и запятую выводить не нужно...
Вполне неплохим решением в этой ситуации будет добавление пустого объекта в самый конец. Для этого просто пропишем его в Конечном тексте.
Теперь, если запустить пресет, получим валидный JSON в виде массива объектов, где каждый объект - это данные по каждому запросу.
При дальнейшей обработке пустой объект можно просто игнорировать.
2.1. Избавляемся от пустого обьекта
Но как быть, если по какой-то причине пустой объект в конце недопустим? Существует небольшой лайфхак. Надо немного изменить логику построчного формирования и выводить запятую не в конце каждой строки, а в начале. И нужно завести переменную, которая будет играть роль флага, указывающего ставить ли запятую или нет. В примере ниже это переменная notFirst.
Итого шаблон для Формата результата будет выглядеть так:
Не забудьте убрать пустой объект из Конечного текста.
В результате получаем валидный JSON без пустых объектов.
Данный метод не имеет никаких ограничений по кол-ву запросов и объёму результатов. Его вполне можно использовать для любых задач, требующих вывод в JSON.
Но существует еще один способ и о нем ниже.
3. Формирование объекта в памяти и вывод результатов в JSON в конце работы задания
Выше мы для нескольких запросов выводили данные в массив. Но как быть, если по какой-то причине массив нам не подходит и нужен один большой объект? И чтобы ключами в этом объекте были подаваемые на вход запросы...
Решение есть. Нужно создать в самом начале работы задания пустой объект, писать в него данные по ходу парсинга и в конце работы задания вывести все это в результат. Поможет все это реализовать опять же функционал Начального и Конечного текстов. Как известно, Начальный отрабатывает в самом начале работы задания, а Конечный - в конце. Этим и воспользуемся.
Итак, в Начальном тексте пропишем создание пустого объекта (используя шаблонизатор):
В Общем формате результата пропишем запись в этот объект:
И в Конечном тексте пропишем вывод в JSON:
Запускаем и получаем JSON в требуемом виде:
Благодаря хранению данных в памяти, можно дополнительно перед выводом в результат произвести с ними различные действия. К примеру отсортировать или произвести подсчет какой-то статистики. Кроме этого данный метод более удобен для логического понимания его работы, в отличие от вышеописанного построчного вывода, который является несколько нетипичным вариантом формирования JSON. Но у этого варианта есть один существенный минус. Его нельзя использовать для очень больших объёмов запросов, т.к. из-за хранения всех данных в памяти можно попросту упереться в ее нехватку. Также итоговый вывод или пост-обработка собранных результатов на большом количестве запросов может занять много времени и привести к ошибкам в работе А-Парсера.
В этой статье мы рассмотрели все варианты вывода результатов в JSON. Какой использовать - зависит от решаемых задач. Оптимальным является построчный вывод, но если нужно сделать какую-то пост-обработку собранных результатов "на лету" - то лучше использовать 3-й метод.
В качестве "подопытного" возьмем парсер Google подсказок
SE::Google::Suggest. Также для большей наглядности условимся что в результате нам необходимы сам запрос, кол-во подсказок по нему, непосредственно сами подсказки и количество символов в каждой подсказке (ее длина).1. Вывод в JSON для одиночных запросов
Простейший вариант вывода в JSON - это использование метода шаблонизатора .json:
Код:
[% obj = {};
obj.query = query;
obj.count = p1.totalcount;
obj.suggests = [];
FOREACH item IN p1.results;
obj.suggests.push({
keyword = item.suggest
length = item.suggest.length
});
END;
obj.json %]
Код:
{"suggests":[{"keyword":"testzentrum","length":11},{"keyword":"teste dich","length":10},{"keyword":"testosterone","length":12},{"keyword":"testzentrum in der nähe","length":23},{"keyword":"testosteron","length":11},{"keyword":"testdaf","length":7},{"keyword":"test center near me","length":19},{"keyword":"testament","length":9}],"count":8,"query":"test"}
2. Формирование JSON построчно
Но что будет, если в предыдущем случае подать на вход несколько запросов? Проверим, добавив для удобства перевод строки после вывода JSON. И попробуем поработать с полученным JSON'ом, вставив его в любой редактор JSON:
Как видно на скриншоте, мы получили ошибку, т.к. JSON не валиден. Сейчас это по сути набор строк, который не отвечает правилам JSON.
Самым очевидным решением по формированию валидного JSON'а для нескольких запросов (да и в целом для любого количества больше одного) является использование массива. Если посмотреть на любой массив в JSON, то он похож на строки, разделенные запятыми. И все это взято в квадратные скобки. Что ж, добавим к выводу запятые (вместе с переводом строки, в формате результата) и квадратные скобки в самом начале и конце (воспользуемся для этого функционалом Начального и Конечного текстов).
Код шаблона в Формате результата по сути остается такой же, как и предыдущем примере, только добавляется запятая после каждой строки (и перевод строки для удобства):
Код:
obj.json _ ",\n"
Получаем такой результат:
Как видим, снова ошибка
Если изучить то, что получилось - становится понятно, что все портит запятая в самом конце (на скрине выделена стрелкой). Но она выводится вместе со строкой JSON'а и нет возможности указать парсеру что какая-то строка последняя и запятую выводить не нужно...
Вполне неплохим решением в этой ситуации будет добавление пустого объекта в самый конец. Для этого просто пропишем его в Конечном тексте.
Теперь, если запустить пресет, получим валидный JSON в виде массива объектов, где каждый объект - это данные по каждому запросу.
При дальнейшей обработке пустой объект можно просто игнорировать.
2.1. Избавляемся от пустого обьекта
Но как быть, если по какой-то причине пустой объект в конце недопустим? Существует небольшой лайфхак. Надо немного изменить логику построчного формирования и выводить запятую не в конце каждой строки, а в начале. И нужно завести переменную, которая будет играть роль флага, указывающего ставить ли запятую или нет. В примере ниже это переменная notFirst.
Итого шаблон для Формата результата будет выглядеть так:
Код:
[% IF notFirst;
",\n";
ELSE;
notFirst = 1;
END;
obj = {};
obj.query = query;
obj.count = p1.totalcount;
obj.suggests = [];
FOREACH item IN p1.results;
obj.suggests.push({
keyword = item.suggest
length = item.suggest.length
});
END;
obj.json %]
В результате получаем валидный JSON без пустых объектов.
Но существует еще один способ и о нем ниже.
3. Формирование объекта в памяти и вывод результатов в JSON в конце работы задания
Выше мы для нескольких запросов выводили данные в массив. Но как быть, если по какой-то причине массив нам не подходит и нужен один большой объект? И чтобы ключами в этом объекте были подаваемые на вход запросы...
Решение есть. Нужно создать в самом начале работы задания пустой объект, писать в него данные по ходу парсинга и в конце работы задания вывести все это в результат. Поможет все это реализовать опять же функционал Начального и Конечного текстов. Как известно, Начальный отрабатывает в самом начале работы задания, а Конечный - в конце. Этим и воспользуемся.
Итак, в Начальном тексте пропишем создание пустого объекта (используя шаблонизатор):
Код:
[% obj = {} %]
Код:
[% obj.${query} = {};
obj.${query}.count = p1.totalcount;
obj.${query}.suggests = [];
FOREACH item IN p1.results;
obj.${query}.suggests.push({
keyword = item.suggest
length = item.suggest.length
});
END %]
Код:
[% obj.json %]
Код:
eJxtVN9v2jAQ/lcii0qtVEWt2r1k2gNlsHVi0EL7BDx45JIaHDuzHboqyv++OwcS
QnmxfHff/fjuzi6Z43ZrnwxYcJZFi5Ll/s4iFkPCC+nYNcu5sWDIvGDzYRT90DqV
EEXzIk3BEuKAXa2uGfrj1Y60yTjFWVwE+s8m7JV/CzAfVfAtKKuvS3WsC9e6UA4t
+W3otOPSy6cgW6eziFus0LhUo+ls2B/8DISDLHickPs+O5ndWe8wL+zbZUn2pdvC
x7s2MUakEAdMbZOgUvd2YgprLSGqK0wynHwPLlasYT3nO3jRyDoRElr1CKUJzwAN
vZg7IGuY+A5dXoXuH/WQx7FwQisu69ZRv9t2viqBRNBfacQSJwF2ZHSGKgc+gCd6
aPuC9bzMMEThfZ9rHxYlXFq4ZhZLHXEsJD61IGHDnTbTnOpBfcm06ks5hh3IFubj
PxRCxrgc/QSdHveO5yHTTzGqht5xqh2Yd4M1NFG89DD93XrFeqzTQzOkyIRD2Q5o
a1B7g8otQN70bEKwTBto0jhTQJMc1z8HFTe76le0M9Z+3gGEG6tVDegw7Eysq1xr
lYh0ityMiOGALNQLvr+pGugsl0CUVSElTszCrN2cvt1PiISmK5+cBz5F5+U6raX9
NUcdPWMjcDO/UIEZNvk46z7kmkv5OhsfW1i7bX7T6HXU/wG+geLm/i7xJ/jz7uh+
708axRoXPtW4isi5WjXfSfPplGc/laiscNAb+1SjiTphUYc9tDhFFt1W/wG6160c
В этой статье мы рассмотрели все варианты вывода результатов в JSON. Какой использовать - зависит от решаемых задач. Оптимальным является построчный вывод, но если нужно сделать какую-то пост-обработку собранных результатов "на лету" - то лучше использовать 3-й метод.