При разработке веб-сервисов и обменов данными в 1С разработчики нередко сталкиваются с ситуацией, когда после упаковки XML-сообщения в ХранилищеЗначения с использованием объекта СжатиеДанных на стороне получателя или при отладке в результирующем XML внезапно отсутствуют отдельные теги. На первый взгляд кажется, что максимальная степень компрессии СжатиеДанных(9) или работа сервера на базе ОС Linux повреждают данные. Разберем по шагам, почему алгоритм сжатия технически не может выборочно удалять XML-элементы, где на самом деле кроется ошибка формирования документа и как правильно передавать оптимизированные пакеты данных.
Проанализируем работу платформы при создании сжатого хранилища. Конструктор Новый ХранилищеЗначения(Данные, Новый СжатиеДанных(9)) использует потоковые алгоритмы сжатия без потерь (семейство Deflate/zlib). Архиватор работает исключительно с непрерывной последовательностью байтов и совершенно ничего не знает о синтаксисе XML, открывающих и закрывающих тегах или иерархии документа.
Если бы в процессе сжатия или распаковки произошел реальный сбой бинарного потока, мы получили бы следующие последствия:
Ситуация, когда из середины документа чисто и валидно исчезает узел (например, тег <Code> со своим значением), алгоритмически невозможна при сбое компрессии. Это означает, что строка XML изначально формируется без этого узла еще до момента помещения в ХранилищеЗначения.
Рассмотрим фрагмент кода, в котором формируется исходное сообщение через фабрику XDTO:
Пока Выборка.Следующий() Цикл
Элемент = Фабрика.Создать(Фабрика.Тип(URI, "CatalogObject.ЗначенияСвойствОбъектов"));
Элемент.Ref = XMLСтрока(Выборка.Ссылка);
Элемент.DeletionMark = XMLСтрока(Выборка.ПометкаУдаления);
Элемент.Owner = Фабрика.Создать(Фабрика.Тип(URI, "CatalogRef.СвойстваОбъектов"), XMLСтрока(Выборка.Владелец));
Элемент.Code = XMLСтрока(Выборка.Код);
Элемент.Description = XMLСтрока(ЗаменитьНедопустимыеСимволыXML(Выборка.Наименование));
Фабрика.ЗаписатьXML(ХМЛ, Элемент,,,,НазначениеТипаXML.Явное);
КонецЦикла;
Выясним причину, по которой узел Code или другие поля могут пропадать из результирующего XML-документа. Здесь действуют три скрытых фактора:
minOccurs="0"): метод ЗаписатьXML объекта ФабрикаXDTO анализирует схему пакета. Если для свойства задано значение minOccurs="0" и в схеме не указан признак nillable="true", то при пустом значении реквизита (пустая строка, Неопределено или значение по умолчанию) фабрика полностью опускает вывод узла в XML, чтобы сэкономить объем документа согласно стандарту XML Schema. Если в выборке попадается элемент с пустым кодом, тег Code просто не сформируется.УПОРЯДОЧИТЬ ПО не гарантируется от вызова к вызову. При разных запусках элементы обходятся в разной последовательности. Разработчик в отладчике открывает случайный элемент из середины файла и видит отсутствие тега, ошибочно связывая это с измененным параметром сжатия, хотя изменился лишь порядок обхода записей.ЗаписьXML не был явно закрыт методом Закрыть(), часть буфера в памяти может быть не сброшена в итоговую строку.Посмотрим на задачу шире. Передача сжатого хранилища значений через веб-сервис на мобильные клиенты с целью уменьшить трафик — это классический антипаттерн платформы 1С. Разберем, почему такая реализация вредит производительности:
ХранилищеЗначения упаковываются в текстовую кодировку Base64. Этот алгоритм неизбежно увеличивает размер передаваемого потока примерно на 33%. Вся выгода от сжатия практически полностью нивелируется.СжатиеДанных(9) практически не дает дополнительного сжатия для текстовых данных по сравнению со стандартным СжатиеДанных(6) (разница обычно не превышает 1–2%), однако алгоритм девятого уровня тратит в разы больше процессорного времени сервера и удерживает контекст памяти.Разберем комплекс мер, которые позволят навести порядок в сериализации данных и добиться максимального сжатия трафика.
Всегда добавляем сортировку в текст запроса, чтобы структура данных была полностью детерминирована:
Запрос.Текст =
"ВЫБРАТЬ
| Спр.Ссылка КАК Ссылка,
| Спр.ПометкаУдаления КАК ПометкаУдаления,
| Спр.Владелец КАК Владелец,
| Спр.Код КАК Код,
| Спр.Наименование КАК Наименование
|ИЗ
| Справочник.ЗначенияСвойствОбъектов КАК Спр
|УПОРЯДОЧИТЬ ПО
| Спр.Ссылка";
Также убираем избыточный вызов функции XMLСтрока() для свойств XDTO, тип которых уже определен в схеме. Фабрика самостоятельно выполняет приведение типов 1С к типам XSD.
Если структура метаданных в конфигурации совпадает со структурой пакета приемника, ручное создание каждого свойства через фабрику не требуется. Применяем объект СериализаторXDTO, который оптимизирован платформой и корректно передает все системные реквизиты:
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку();
Пока Выборка.Следующий() Цикл
Объект1С = Выборка.Ссылка.ПолучитьОбъект();
СериализаторXDTO.ЗаписатьXML(ЗаписьXML, Объект1С, НазначениеТипаXML.Явное);
КонецЦикла;
ИтоговыйXML = ЗаписьXML.Закрыть();
Самый эффективный способ сократить трафик мобильных клиентов — отказаться от оборачивания данных в ХранилищеЗначения внутри кода 1С. Веб-сервис должен отдавать обычный XML или JSON, а компрессию необходимо делегировать веб-серверу (Nginx или Apache), через который опубликована база 1С.
При использовании стандартного механизма HTTP-сжатия (Gzip или Brotli) веб-сервер сжимает данные на лету только для тех клиентов, которые поддерживают заголовок Accept-Encoding: gzip. Данные передаются без преобразования в Base64, трафик минимизируется на 70–80%, а клиентские устройства распаковывают ответ стандартными сетевыми библиотеками операционной системы.
Если для обмена критичен минимальный вес пакета, оптимальным решением будет отказ от формата XML в пользу JSON с использованием объекта ЗаписьJSON, что дополнительно уменьшит объем полезной нагрузки на треть за счет отсутствия закрывающих тегов.