Как прочитать XML из Хранилища Значений в 1С без записи файла на диск?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке интеграционных решений, обмене данными или работе с прикрепленными файлами в 1С:Предприятии программисты часто сталкиваются с необходимостью обработки XML-документов, хранящихся в базе данных. Традиционный, но устаревший подход заключается во временной выгрузке бинарных данных на жесткий диск во временный каталог, чтении этого файла и его последующем удалении. Этот метод прост в реализации, однако имеет ряд критических недостатков.

В этой статье мы подробно разберем, почему запись временных файлов на диск замедляет систему, проанализируем причины популярных ошибок парсинга XML (таких как фатальные сбои кодировки и ошибки сериализации XDTO) и рассмотрим современные, высокопроизводительные способы чтения данных непосредственно из оперативной памяти.

Почему запись временных файлов на диск — это плохая практика?

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

  1. Низкая производительность (I/O Bottleneck). Операции чтения и записи на жесткий диск (даже на быстрые NVMe SSD) выполняются в тысячи раз медленнее, чем аналогичные операции в оперативной памяти. При высокой интенсивности обменов запись файлов создает избыточную нагрузку на дисковую подсистему сервера приложений.
  2. Проблемы с правами доступа. На серверном кластере 1С сеансы пользователей выполняются под технической учетной записью (например, usr1cv8). Если у этой учетной записи настроены жесткие ограничения прав на запись в системные папки или временные каталоги, выгрузка файла завершится аварийно с ошибкой доступа.
  3. Конкурентный доступ и «мусор». При одновременной работе десятков пользователей под одной учетной записью операционной системы в одном временном каталоге могут возникнуть конфликты имен файлов. Кроме того, в случае аварийного завершения сеанса временные файлы остаются на диске, постепенно переполняя свободное пространство сервера.

Именно поэтому чтение XML-данных напрямую из объекта ХранилищеЗначений в оперативную память является стандартом качественной разработки в системе 1С:Предприятие.

Разбор типичных ошибок при чтении XML из двоичных данных

При попытке реализовать чтение в памяти «напрямую» разработчики часто совершают типовые ошибки. Рассмотрим две самые распространенные из них, с которыми столкнулся автор темы на форуме.

1. Ошибка «Attribute ???????? redefined» (Атрибут переопределен)

Эта фатальная ошибка парсера XML возникает в процессе десериализации данных. Давайте выясним ее физическую причину. Автор темы пытался считать бинарные данные, используя следующий код:


ЧтениеДанных = Новый ЧтениеДанных(ДвоичныеДанные);
СтрокаXML = ЧтениеДанных.ПрочитатьСимволы(, КодировкаТекста.ANSI);

Причина ошибки: Несовпадение кодировок. Подавляющее большинство современных XML-файлов формируются в кодировке UTF-8 или UTF-16 (Unicode). Когда мы пытаемся прочитать файл формата UTF-8, используя кодировку ANSI (которая ориентирована на локальную кодовую страницу системы, например, Windows-1251), русские буквы превращаются в нечитаемые символы. Очень часто парсер заменяет неопознанные символы знаками вопроса ?.

Если в исходном XML-документе в одном элементе (теге) присутствовали разные атрибуты на кириллице, например, Имя="Товар" и Код="001", то после некорректного декодирования в ANSI парсер увидит следующую картину: ???="Товар" ???="001". По спецификации XML, внутри одного тега категорически запрещено дублирование атрибутов с одинаковыми именами. Парсер обнаруживает два атрибута с именем ??? и выдает ошибку: «Attribute ???????? redefined».

Решение: Всегда контролируйте кодировку исходного XML-документа и явно указывайте ее при чтении (чаще всего это КодировкаТекста.UTF8).

2. Ошибка преобразования данных XDTO

Вторая распространенная ошибка выглядит так: Ошибка преобразования данных XDTO: НачалоСвойства: Файл Форма: Элемент Тип....

Причина ошибки: Использование неподходящего программного инструмента. В попытках распарсить полученный XML-документ разработчики часто вызывают метод СериализаторXDTO.ПрочитатьXML(). Однако объект СериализаторXDTO предназначен исключительно для работы с каноническим XML-форматом платформы 1С. Он используется для сериализации и десериализации ссылочных типов (справочники, документы), регистров, а также стандартных коллекций (структуры, массивы, соответствия), которые были выгружены встроенными средствами 1С.

Если ваш XML-документ является произвольным (например, это файл выгрузки коммерческих предложений CommerceML, накладная от контрагента или произвольная структура стороннего веб-сервиса), СериализаторXDTO не сможет сопоставить его структуру с типами метаданных конфигурации 1С и выдаст ошибку.

Решение: Для чтения произвольных XML-документов следует использовать объект ФабрикаXDTO и ее метод ПрочитатьXML(), передавая туда подготовленный объект чтения XML.

Эффективные способы чтения XML из Хранилища Значений

Рассмотрим два наиболее оптимальных архитектурных решения, которые позволяют полностью отказаться от записи файлов на диск и корректно обрабатывают кодировки и структуры данных.

Способ А. Использование файловых потоков в памяти (для платформы 8.3.10 и выше)

Начиная с версии платформы 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-документами в оперативной памяти рекомендуем придерживаться следующих правил:

← На главную