gifts2017

Парсер JSON (Штатные средства 1С 8.3.6)

Опубликовал {ÐƦǑƝȊ} mx (dour-dead) в раздел Программирование - Практика программирования

Хочу поделиться функцией чтения json, реализованной с помощью штатных средств платформы.
Совсем недавно, начиная с платформы 8.3.6.1977, фирмой "1С" были реализованы штатные средства для работы с JSON.
Но, наверно, многие уже давно привыкли работать с этим форматом обмена данных.
Впервые столкнувшись с необходимостью работы с json я наткнулся на замечательную обработку 1С:JSON от Переверзева Александра , огромное спасибо этому человеку, сэкономил кучу времени. Все бы было хорошо, пока не потребовалась обработка больших пакетов данных.

Трудности

Однажды пришлось столкнуться с обработкой больших пакетов данных, и обработка пакета 2.2 мб за ~ 60 секунд, для нас это было долго.

Решили опробовать штаного зверя, обход такого пакета данных по средствам чтения занял ~ 1-1.5 сек.

Пока ЧтениеJSON.Прочитать() Цикл

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

Результат

В итоге обработка такого пакета данных в 2.2 мб с возвратом уже привычной структуры заняла ~ 2.5 секунды.

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

Пример части  кода 1С:JSON и штаного парсера ниже.

// JSON парсер.
&НаКлиенте
Функция ПрочитатьJSONИзФайла(Значение, Стандарт = Истина, ПредставленияСсылок = Ложь) Экспорт 
    
    Попытка
        ЧтениеJSON = Новый ЧтениеJSON;
        ЧтениеJSON.Закрыть();
        
        Возврат jsonПрочитатьПлатформой(Значение);
    Исключение
        Возврат jsonПрочитатьИнициализация(Значение, Стандарт, ПредставленияСсылок);
    КонецПопытки;
    
КонецФункции // ПрочитатьJSON()

&НаКлиенте
Функция jsonПрочитатьПлатформой(Значение)

    ЧтениеJSON = Новый ЧтениеJSON;
    ЧтениеJSON.УстановитьСтроку(Значение);
    
    Результат = Неопределено;
    СформироватьДерево(ЧтениеJSON, Результат);
    
    ЧтениеJSON.Закрыть();
    
    Возврат Результат;

КонецФункции

&НаКлиенте
Процедура СформироватьДерево(ЧтениеJSON, Дерево)
    
    ИмяСвойства = Неопределено;
    
    Пока ЧтениеJSON.Прочитать() Цикл
        TипJSON = ЧтениеJSON.ТипТекущегоЗначения;
        
        Если TипJSON = ТипЗначенияJSON.НачалоОбъекта 
        ИЛИ TипJSON = ТипЗначенияJSON.НачалоМассива Тогда
            НовыйОбъект = ?(TипJSON = ТипЗначенияJSON.НачалоОбъекта, Новый Соответствие, Новый Массив);
            
            Если ТипЗнч(Дерево) = Тип("Массив") Тогда
                Дерево.Добавить(НовыйОбъект);
            ИначеЕсли ТипЗнч(Дерево) = Тип("Соответствие") И ЗначениеЗаполнено(ИмяСвойства) Тогда
                Дерево.Вставить(ИмяСвойства, НовыйОбъект);
            КонецЕсли;
            
            СформироватьДерево(ЧтениеJSON, НовыйОбъект);
            
            Если Дерево = Неопределено Тогда
                Дерево = НовыйОбъект;
            КонецЕсли;
        ИначеЕсли TипJSON = ТипЗначенияJSON.ИмяСвойства Тогда
            ИмяСвойства = ЧтениеJSON.ТекущееЗначение;
        ИначеЕсли TипJSON = ТипЗначенияJSON.Число 
        ИЛИ TипJSON = ТипЗначенияJSON.Строка 
        ИЛИ TипJSON = ТипЗначенияJSON.Булево 
        ИЛИ TипJSON = ТипЗначенияJSON.Null Тогда
            Если ТипЗнч(Дерево) = Тип("Массив") Тогда
                Дерево.Добавить(ЧтениеJSON.ТекущееЗначение);
            ИначеЕсли ТипЗнч(Дерево) = Тип("Соответствие") Тогда
                Дерево.Вставить(ИмяСвойства, ЧтениеJSON.ТекущееЗначение);
            КонецЕсли;
        Иначе
            Возврат;
        КонецЕсли;
    КонецЦикла;
    
КонецПроцедуры

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

См. также

Подписаться Добавить вознаграждение
Комментарии
1. Антонио (Fragster) 29.09.16 18:20
Глобальный контекст.ПрочитатьJSON (Global context.ReadJSON)
Глобальный контекст (Global context)
ПрочитатьJSON (ReadJSON)
Синтаксис:

ПрочитатьJSON(<ЧтениеJSON>, <ПрочитатьВСоответствие>, <ИменаСвойствСоЗначениямиДата>, <ОжидаемыйФорматДаты>, <ИмяФункцииВосстановления>, <МодульФункцииВосстановления>, <ДополнительныеПараметрыФункцииВосстановления>, <ИменаСвойствДляОбработкиВосстановления>, <МаксимальнаяВложенность>)
Параметры:

<ЧтениеJSON> (обязательный)

Тип: ЧтениеJSON.
Объект чтения JSON.
<ПрочитатьВСоответствие> (необязательный)

Тип: Булево.
Если установлено Истина, чтение объекта JSON будет выполнено в Соответствие.
Если установлено Ложь, объекты будут считываться в объект типа Структура.
Примечание. При десериализации объектов JSON в структуру необходимо помнить о требованиях к ключам структуры. Если при десериализации объекта будет найдено имя свойства, недопустимое для ключа структуры, то будет вызвано исключение.
Значение по умолчанию: Ложь.
<ИменаСвойствСоЗначениямиДата> (необязательный)

Тип: Массив, Строка, ФиксированныйМассив.
Массив, элементы которого содержат имена свойств JSON. Для указанных свойств будет вызвано восстановление даты из строки согласно формату, указанному в параметре ОжидаемыйФорматДаты.
Если имя свойства указано в этом параметре и в параметре ИменаСвойствДляОбработкиВосстановления, то для таких свойств восстановление осуществляется в функции восстановления.
Если восстановление даты из значения свойства невозможно, то будет сгенерировано исключение.
Значение по умолчанию: Неопределено.
<ОжидаемыйФорматДаты> (необязательный)

Тип: ФорматДатыJSON.
Ожидаемый формат даты при десериализации объекта в формате JSON.
Если десериализуемое значение не является строкой и имеет формат даты, отличный от ожидаемого, то будет вызвано исключение.
Значение по умолчанию: ISO.
<ИмяФункцииВосстановления> (необязательный)

Тип: Строка.
Данная функция вызывается при чтении каждого свойства и должна иметь следующие параметры:
<Свойство> - значение типа Строка, указывается только при чтении объектов JSON,
<Значение> - значение допустимого для сериализации типа,
<ДополнительныеПараметры>.
Возвращаемое значение - произвольного типа.
Если данный параметр задан и не задан параметр МодульФункцииВосстановления, и наоборот, будет вызвано исключение.
Если функция не установлена, то при вызове метода ПрочитатьJSON, параметр ИменаСвойствСоЗначениямиДата игнорируется.
Значение по умолчанию: Неопределено.
<МодульФункцииВосстановления> (необязательный)

Тип: УправляемаяФорма; КомандаКомандногоИнтерфейса; ОбщийМодуль.
Указывает модуль, процедура которого будет использована для восстановления значения. В зависимости от типа параметра будет вызван соответствующий метод:
УправляемаяФорма - будет вызван метод модуля указанной управляемой формы.
КомандаКомандногоИнтерфейса - будет вызван метод модуля команды командного интерфейса.
ОбщийМодуль - будет вызван метод неглобального общего модуля.

Значение по умолчанию: Неопределено.
<ДополнительныеПараметрыФункцииВосстановления> (необязательный)

Тип: Произвольный.
Дополнительные параметры, которые будут переданы в функцию восстановления значений.
Значение по умолчанию: Неопределено.
<ИменаСвойствДляОбработкиВосстановления> (необязательный)

Тип: Массив.
Массив имен свойств JSON, для которых будет вызвана функция восстановления.
Параметр игнорируется, если не установлен параметр ИмяФункцииВосстановления.
Значение по умолчанию: Неопределено.
<МаксимальнаяВложенность> (необязательный)

Тип: Число.
Максимальный уровень вложенности объекта JSON.
При превышении уровня вложенности будет сгенерировано исключение.
Значение по умолчанию: 500.
Возвращаемое значение:

Тип: Произвольный.

Описание:

Считывает значение из JSON-текста или файла. JSON-текст должен быть корректным.

Доступность:

Тонкий клиент, сервер, толстый клиент, внешнее соединение.
Примечание:

Массив будет десеарилизован в массив. Объект JSON будет преобразован в соответствие или структуру (если ключ структуры окажется недопустимым, будет вызвано исключение).
Для дат действует аналогично методу ПрочитатьДатуJSON.
Во время выполнения метода может быть вызвана пользовательская функция для восстановления значения - для этого следует использовать параметр ИмяФункцииВосстановления. Функция восстановления должна быть описана с директивой &НаСервере или &НаКлиенте. Использование функции вне контекста не допускается.
--------------------------------------------------------------------------------

Методическая информация
dark_wolf; dour-dead; vano-ekt; +3 Ответить 2
2. Павел Толкачев (ltfriend) 30.09.16 06:37
3. Антонио (Fragster) 30.09.16 11:51
(2) ltfriend, это цитата из синтакс-помощника про встроенный в платформу метод, который делает то же самое, что и код из статьи.
4. Петр Цап (Inkasor) 30.09.16 12:51
(3) Fragster, там не всё так просто с этим методом, он существует, но не очень быстро работает :) я как раз на INFOSTART EVENT DEVELOPER 2016 хочу уделить этому некоторое время в докладе :) Впрочем, мы измеряем скорость по другому, не в потоке мб/с, а в количестве объектов обмена в секунду.
5. Антонио (Fragster) 30.09.16 13:14
(4) Inkasor, есть подозрение, что указанный в статье способ еще более медленный, чем платформенный. причем платформенный позволяет еще и некоторые преобразования данных провернуть. Например у меня так структуры вида XMLТип + гуид преобразуются в ссылки на объекты метаданных.
6. Петр Цап (Inkasor) 30.09.16 13:30
(5) Fragster, "было принято решение сделать аналогичную структуру", legacy же :) мы у себя по другому разбираем. Там ещё очень важный момент с тем, какими объектами мы друг с другом обмениваемся. Если делать обмен 1С-1С, ПрочитатьJSON() возможно будет самым лучшим решением, но если 1С-что-то другое, тогда ЧтениеJSON.ТекущееЗначение возможно, будет быстрей, тут всё зависит от структуры объекта обмена. Про ЗаписатьJSON вообще молчу :)
7. Павел Толкачев (ltfriend) 30.09.16 23:55
(3) т.е. статю вы не читали?
8. {ÐƦǑƝȊ} mx (dour-dead) 01.10.16 12:10
(1) Fragster, Спасибо (будем юзать штатный метод)! Как то не внимательно значит я читал новые возможности для работы с JSON.
Сделал замеры по времени на разных видах данных, частично метод проигрывает от 2 до 5 раз) .

Прикрепленные файлы:
9. Антонио (Fragster) 01.10.16 22:12
(8) dour-dead, ну, судя по замерам - во всех случаях от 5 до 10 раз. просто надо выводить одинаковое количество знаков после запятой, чтобы в глаза бросилось.
10. ПОБЕДАСОФТ (prog77) 05.10.16 10:16
Спасибо за функцию, она не чувствительна к именам свойств в отличии от типовой, в которой - "Если при десериализации объекта будет найдено имя свойства, недопустимое для ключа структуры, то будет вызвано исключение."
11. Антонио (Fragster) 05.10.16 11:23
(10) prog77, вы плохо прочитали синтакс-помощник.
12. Ruslan (rus128) 05.10.16 12:58
Заметил опечатку:
"Пример части кода 1С:JSON и шта(Т)ного парсера ниже."
13. Василий Василий (VasilVtoroy) 08.10.16 00:28
(10) prog77, есть параметр, который позволяет читать в Соответствие и тогда проблемы нет
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа