При разработке интеграционных решений, обмене данными или работе с прикрепленными файлами в 1С:Предприятии программисты часто сталкиваются с необходимостью обработки XML-документов, хранящихся в базе данных. Традиционный, но устаревший подход заключается во временной выгрузке бинарных данных на жесткий диск во временный каталог, чтении этого файла и его последующем удалении. Этот метод прост в реализации, однако имеет ряд критических недостатков.
В этой статье мы подробно разберем, почему запись временных файлов на диск замедляет систему, проанализируем причины популярных ошибок парсинга XML (таких как фатальные сбои кодировки и ошибки сериализации XDTO) и рассмотрим современные, высокопроизводительные способы чтения данных непосредственно из оперативной памяти.
Прежде чем перейти к программным решениям, давайте разберем, почему профессиональные разработчики стараются избегать работы с дисковой подсистемой без крайней необходимости:
usr1cv8). Если у этой учетной записи настроены жесткие ограничения прав на запись в системные папки или временные каталоги, выгрузка файла завершится аварийно с ошибкой доступа.Именно поэтому чтение XML-данных напрямую из объекта ХранилищеЗначений в оперативную память является стандартом качественной разработки в системе 1С:Предприятие.
При попытке реализовать чтение в памяти «напрямую» разработчики часто совершают типовые ошибки. Рассмотрим две самые распространенные из них, с которыми столкнулся автор темы на форуме.
Эта фатальная ошибка парсера XML возникает в процессе десериализации данных. Давайте выясним ее физическую причину. Автор темы пытался считать бинарные данные, используя следующий код:
ЧтениеДанных = Новый ЧтениеДанных(ДвоичныеДанные);
СтрокаXML = ЧтениеДанных.ПрочитатьСимволы(, КодировкаТекста.ANSI);
Причина ошибки: Несовпадение кодировок. Подавляющее большинство современных XML-файлов формируются в кодировке UTF-8 или UTF-16 (Unicode). Когда мы пытаемся прочитать файл формата UTF-8, используя кодировку ANSI (которая ориентирована на локальную кодовую страницу системы, например, Windows-1251), русские буквы превращаются в нечитаемые символы. Очень часто парсер заменяет неопознанные символы знаками вопроса ?.
Если в исходном XML-документе в одном элементе (теге) присутствовали разные атрибуты на кириллице, например, Имя="Товар" и Код="001", то после некорректного декодирования в ANSI парсер увидит следующую картину: ???="Товар" ???="001". По спецификации XML, внутри одного тега категорически запрещено дублирование атрибутов с одинаковыми именами. Парсер обнаруживает два атрибута с именем ??? и выдает ошибку: «Attribute ???????? redefined».
Решение: Всегда контролируйте кодировку исходного XML-документа и явно указывайте ее при чтении (чаще всего это КодировкаТекста.UTF8).
Вторая распространенная ошибка выглядит так: Ошибка преобразования данных XDTO: НачалоСвойства: Файл Форма: Элемент Тип....
Причина ошибки: Использование неподходящего программного инструмента. В попытках распарсить полученный XML-документ разработчики часто вызывают метод СериализаторXDTO.ПрочитатьXML(). Однако объект СериализаторXDTO предназначен исключительно для работы с каноническим XML-форматом платформы 1С. Он используется для сериализации и десериализации ссылочных типов (справочники, документы), регистров, а также стандартных коллекций (структуры, массивы, соответствия), которые были выгружены встроенными средствами 1С.
Если ваш XML-документ является произвольным (например, это файл выгрузки коммерческих предложений CommerceML, накладная от контрагента или произвольная структура стороннего веб-сервиса), СериализаторXDTO не сможет сопоставить его структуру с типами метаданных конфигурации 1С и выдаст ошибку.
Решение: Для чтения произвольных XML-документов следует использовать объект ФабрикаXDTO и ее метод ПрочитатьXML(), передавая туда подготовленный объект чтения XML.
Рассмотрим два наиболее оптимальных архитектурных решения, которые позволяют полностью отказаться от записи файлов на диск и корректно обрабатывают кодировки и структуры данных.
Начиная с версии платформы 1С:Предприятие 8.3.10, разработчикам стал доступен мощный механизм работы с потоками данных. Это наиболее прогрессивный и производительный способ решения задачи, так как данные передаются «на лету» без избыточного выделения памяти под промежуточные строки большого объема.
Давайте разберем этот процесс по шагам в процессе совместного написания кода:
// Шаг 1. Извлекаем двоичные данные файла из Хранилища Значений регистра или реквизита
ДвоичныеДанные = ВыборкаРаз.ДвоичныеДанныеФайла.Получить();
Если ТипЗнч(ДвоичныеДанные) <> Тип("ДвоичныеДанные") Тогда
ВызватьИсключение "В хранилище значений отсутствуют корректные двоичные данные!";
КонецЕсли;
// Шаг 2. Открываем поток для чтения.
// Внимание: круглые скобки в конце метода обязательны, иначе платформа выдаст ошибку!
ПотокЧтения = ДвоичныеДанные.ОткрытьПотокДляЧтения();
// Шаг 3. Инициализируем объект чтения XML
ЧтениеXML = Новый ЧтениеXML;
// Шаг 4. Связываем чтение XML напрямую с открытым потоком в памяти
ЧтениеXML.ОткрытьПоток(ПотокЧтения);
Попытка
// Шаг 5. Используем глобальную ФабрикуXDTO для чтения произвольной структуры XML
ОбъектXDTO = ФабрикаXDTO.ПрочитатьXML(ЧтениеXML);
// Здесь мы можем работать со свойствами объекта XDTO напрямую, например:
// ИмяФайла = ОбъектXDTO.ИдФайл;
ПроверитьИОбработатьДанныеXDTO(ОбъектXDTO);
Исключение
// Шаг 6. Обязательно закрываем потоки при возникновении ошибок для предотвращения утечек памяти
ЧтениеXML.Закрыть();
ПотокЧтения.Закрыть();
ВызватьИсключение "Ошибка при разборе XML структуры: " + ОписаниеОшибки();
КонецПопытки;
// Шаг 7. Корректно завершаем работу с объектами
ЧтениеXML.Закрыть();
ПотокЧтения.Закрыть();
Этот способ идеален для работы с файлами средних и крупных размеров, так как оптимизирует потребление оперативной памяти на сервере приложений 1С.
Если ваша база данных по какой-то причине работает на устаревшей версии платформы (ниже 8.3.10), или если вы хотите написать максимально универсальный код, не зависящий от поддержки потоков, можно воспользоваться методом конвертации двоичных данных в строку. Для обеспечения совместимости при работе с двоичными данными на разных версиях платформы также подойдёт библиотека для работы с двоичными данными в 1С 8.х.
Давайте посмотрим, как реализовать этот алгоритм:
// Шаг 1. Получаем двоичные данные из хранилища
ДвоичныеДанные = ВыборкаРаз.ДвоичныеДанныеФайла.Получить();
Если ЗначениеЗаполнено(ДвоичныеДанные) Тогда
// Шаг 2. Преобразуем двоичные данные в строку.
// Важно: явно указываем кодировку UTF-8, чтобы избежать ошибок декодирования кириллицы!
СтрокаXML = ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные, КодировкаТекста.UTF8);
// Шаг 3. Инициализируем чтение XML
ЧтениеXML = Новый ЧтениеXML;
// Шаг 4. Загружаем строку непосредственно в парсер
ЧтениеXML.УстановитьСтроку(СтрокаXML);
Попытка
// Шаг 5. Читаем данные через ФабрикуXDTO
ОбъектXDTO = ФабрикаXDTO.ПрочитатьXML(ЧтениеXML);
// Обработка полученного объекта
ОбработатьОбъект(ОбъектXDTO);
Исключение
ЧтениеXML.Закрыть();
ЗаписатьЛогОшибок(ОписаниеОшибки());
ВызватьИсключение "Не удалось прочитать структуру XML из строки.";
КонецПопытки;
// Шаг 6. Закрываем объект чтения
ЧтениеXML.Закрыть();
КонецЕсли;
Использование встроенной функции ПолучитьСтрокуИзДвоичныхДанных() работает значительно быстрее, чем ручное посимвольное чтение через объект ЧтениеДанных, и полностью исключает вероятность ошибок дублирования атрибутов («Attribute redefined»), так как кодировка текста обрабатывается платформой корректно.
Для эффективной работы с XML-документами в оперативной памяти рекомендуем придерживаться следующих правил:
ФабрикаXDTO. Метод СериализаторXDTO оставляйте только для канонических объектов сериализации 1С.КодировкаТекста.UTF8), чтобы не столкнуться с порчей символов кириллицы и ошибками парсера.