Чтение XML в 1С: Как последовательно прочитать только нужные узлы через Фабрику XDTO без пропуска элементов?

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

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

В таких случаях фирма 1С рекомендует использовать гибридный подход: последовательно перемещаться по файлу с помощью объекта ЧтениеXML, а преобразовывать в объекты XDTO только целевые узлы. Но при реализации этого алгоритма начинающие программисты часто сталкиваются с неприятной проблемой — парсер начинает считывать элементы через один. В этой статье мы подробно разберем, почему возникает эта ошибка, как устроен механизм позиционирования указателя, и рассмотрим несколько эффективных способов решения задачи.

Выясняем причину: почему элементы считываются через один?

Рассмотрим классический пример ошибочной реализации, который часто встречается на практике:


Пока ЧтениеХМЛ.Прочитать() Цикл
    
    Если ЧтениеХМЛ.ЛокальноеИмя = "patient" И ЧтениеХМЛ.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
        
        // Читаем элемент в объект XDTO
        Данные = ФабрикаXDTO.ПрочитатьXML(ЧтениеХМЛ, лТип);
        Сообщить(Данные.surname);
        
    КонецЕсли;
    
КонецЦикла;

На первый взгляд код выглядит логичным. Мы перебираем узлы методом Прочитать(), находим начало нужного элемента, десериализуем его и идем дальше. Однако при запуске этого кода мы обнаружим, что программа вывела данные 1-го, 3-го, 5-го пациентов, проигнорировав чётные элементы. Почему так происходит?

Разберем поведение указателя XML-потока по шагам:

  1. Метод ЧтениеХМЛ.Прочитать() находит первый открывающий тег <patient>. Указатель позиционируется на типе узла НачалоЭлемента.
  2. Выполняется условие Если, и вызывается метод ФабрикаXDTO.ПрочитатьXML().
  3. Объект ФабрикаXDTO начинает самостоятельно считывать данные из потока ЧтениеXML. Он обрабатывает все вложенные реквизиты пациента и останавливается только тогда, когда полностью закроет тег </patient>. Указатель в объекте ЧтениеXML теперь позиционируется сразу за закрывающим тегом прочитанного элемента (на границе следующего узла).
  4. Управление возвращается в цикл Пока, где первым делом снова вызывается метод ЧтениеХМЛ.Прочитать().
  5. Этот принудительный вызов сдвигает указатель еще раз вперед. Таким образом, парсер перескакивает через открывающий тег <patient> следующего элемента, переходя к его внутреннему содержимому или вовсе к третьему элементу.

В результате такого "двойного" сдвига мы теряем ровно половину данных. Вызов метода Пропустить(), как пытался сделать автор темы, только усугубляет ситуацию, так как он предназначен для пропуска всей ветки текущего узла и также сдвигает указатель.

Давайте разберем правильные архитектурные решения этой проблемы.

Решение 1. Реализация через вложенный цикл (Классический вариант)

Чтобы избежать нежелательного вызова Прочитать() на итерации внешнего цикла, мы можем использовать вложенный цикл Пока. Этот подход позволяет обрабатывать идущие друг за другом однотипные элементы непрерывно, пока указатель позиционирован на них.

Проанализируем следующий код:


ВсегоПациентов = 0;

Пока ЧтениеХМЛ.Прочитать() Цикл
    
    // Внутренний цикл обрабатывает пачку элементов подряд
    Пока ЧтениеХМЛ.ЛокальноеИмя = "patient" И ЧтениеХМЛ.ТипУзла = ТипУзлаXML.НачалоЭлемента Цикл
        
        ВсегоПациентов = ВсегоПациентов + 1;
        
        // Метод считывает элемент целиком и сдвигает указатель на начало следующего узла
        Данные = ФабрикаXDTO.ПрочитатьXML(ЧтениеХМЛ, лТип);
        
        Сообщить(Данные.surname);
        
    КонецЦикла;
    
КонецЦикла;

Как это работает по шагам:

  1. Внешний цикл сдвигает указатель до первого элемента patient.
  2. Внутренний цикл проверяет соответствие имени и типа узла. Условие выполняется, управление передается внутрь.
  3. Вызывается ФабрикаXDTO.ПрочитатьXML(). Объект успешно считывается, а указатель автоматически встает на начало следующего узла.
  4. Внутренний цикл проверяет условие снова. Так как указатель уже стоит на начале следующего patient, нам не нужно вызывать метод Прочитать(). Программа сразу переходит к десериализации второго объекта.
  5. Когда элементы списка закончатся (например, парсер дойдет до закрывающего тега списка </patients>), условие внутреннего цикла вернет Ложь. Управление вернется во внешний цикл, который безопасно выполнит метод Прочитать() для продвижения по структуре документа дальше.

Решение 2. Цикл «Пока Истина» с ручным управлением указателем

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

Рассмотрим пример реализации:


Пока Истина Цикл
    
    // Проверяем, стоим ли мы на нужном элементе
    Если ЧтениеХМЛ.ЛокальноеИмя = "patient" И ЧтениеХМЛ.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
        
        // Чтение элемента. Метод ПрочитатьXML() сам сдвинет указатель на следующий узел
        Данные = ФабрикаXDTO.ПрочитатьXML(ЧтениеХМЛ, лТип);
        Сообщить(Данные.surname);
        
    Иначе
        
        // Сдвигаем указатель вперед, только если мы не читали данные через Фабрику
        Если Не ЧтениеХМЛ.Прочитать() Тогда
            Прервать; // Достигнут конец XML-документа
        КонецЕсли;
        
    КонецЕсли;
    
КонецЦикла;

Данный алгоритм максимально прозрачен: мы либо считываем объект XDTO (автоматически продвигая указатель потока), либо принудительно делаем шаг вперед с помощью ЧтениеХМЛ.Прочитать(), если текущий узел — это технический тег, пробел или перенос строки.

Решение 3. Использование глобальной функции «ВозможностьЧтенияXML»

Для конфигураций, в которых активно используется стандартный механизм сериализации 1С, существует еще более элегантное и лаконичное решение. Мы можем задействовать встроенную системную функцию ВозможностьЧтенияXML().

Посмотрим на пример применения этого метода:


Пока ЧтениеХМЛ.Прочитать() Цикл
    
    // Метод проверяет, соответствуют ли текущие данные типу XDTO
    Пока ВозможностьЧтенияXML(ЧтениеХМЛ) Цикл
        
        // Автоматическое чтение объекта без ручного указания типа
        Данные = ПрочитатьXML(ЧтениеХМЛ);
        Сообщить(Данные.surname);
        
    КонецЦикла;
    
КонецЦикла;

Этот способ избавляет разработчика от необходимости вручную прописывать текстовые проверки имени узла (ЛокальноеИмя = "patient") и типа узла, перекладывая эту задачу на плечи платформы 1С. Однако помните, что для работы этого метода схема XML должна быть известна конфигурации (например, добавлена в XDTO-пакеты).

Важнейшая оптимизация: Игнорирование пробельных символов

При работе с файлами XML, предназначенными для чтения человеком, часто используются отступы, знаки табуляции и переводы строк. Для платформы 1С каждый такой символ является отдельным узлом с типом ТипУзлаXML.Текст или ТипУзлаXML.ЗначимыеПробелы.

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

Давайте разберем, как правильно инициализировать чтение с отключением обработки пробелов:


// Создаем параметры чтения. 9-й параметр отвечает за игнорирование пробельных символов
ПараметрыЧтения = Новый ПараметрыЧтенияXML(,,,,,,,, Истина);

ЧтениеХМЛ = Новый ЧтениеXML;
ЧтениеХМЛ.ОткрытьФайл(ПутьКФайлу, ПараметрыЧтения);

// Теперь парсер будет перемещаться исключительно по значащим тегам структуры XML!

Использование свойства игнорирования пробелов не только повышает скорость работы алгоритма (иногда до 2-3 раз на больших объемах данных), но и гарантирует, что указатель после вызова ПрочитатьXML() будет позиционироваться непосредственно на начале следующего тега, сводя к минимуму вероятность логических ошибок при чтении.

Применяйте рассмотренные шаблоны проектирования в своих интеграционных решениях, и ваши обработки импорта XML-данных будут работать быстро, стабильно и без лишней нагрузки на серверную память!

← На главную