Как проверить атрибут XML на пустоту и найти дубли по ИНН при загрузке данных через XDTO

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

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

Понимание структуры объекта XDTO

Прежде чем приступать к написанию кода, проанализируем ситуацию. Когда мы читаем XML-файл с помощью фабрики XDTO, платформа создает иерархическую структуру объектов. Важно понимать, что обращение к реквизиту через точку, например Элемент.Телефоны, возвращает не строку и не число, а именно объект типа ОбъектXDTO или значение, типизированное согласно схеме XSD.

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

Проверка наличия и заполненности свойств

Для того чтобы безопасно работать с данными из XML, мы рекомендуем использовать метод Свойство(). Этот метод позволяет проверить, существует ли вообще такое поле в объекте, не вызывая аварийного завершения программы. Выясним, как это реализовать на практике.

Рассмотрим по шагам алгоритм проверки:

  1. Проверяем наличие свойства в объекте XDTO.
  2. Проверяем, не является ли значение Неопределено.
  3. Используем универсальную функцию ЗначениеЗаполнено() для проверки фактического содержания.

// Предположим, 'Элемент' — это наш ОбъектXDTO
ИмяСвойства = "Телефоны";
Если Элемент.Свойства().Получить(ИмяСвойства) <> Неопределено Тогда
    ЗначениеИзXML = Элемент[ИмяСвойства];
    
    // Проверяем, заполнено ли значение фактически
    Если ЗначениеЗаполнено(ЗначениеИзXML) Тогда
        НовыйКонтрагент.Телефоны = Строка(ЗначениеИзXML);
    КонецЕсли;
КонецЕсли;

В данном примере мы используем обращение по индексу Элемент[ИмяСвойства], что позволяет писать более гибкий код. Обратите внимание на явное приведение к типу Строка(). Это "хороший тон" при работе с XDTO, так как позволяет избежать конфликтов типов при записи в реквизит справочника.

Поиск дублей по ИНН или другим реквизитам

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

Проанализируем правильный подход к поиску. Метод НайтиПоРеквизиту() всегда возвращает ссылку. Если элемент не найден, возвращается пустая ссылка соответствующего типа. Поэтому проверка Если НайтиПоРеквизиту(...).Пустая() является ключевой.


// Получаем ИНН из объекта XDTO
ТекущийИНН = Строка(Элемент.ИНН);

// Выполняем поиск в справочнике Контрагенты
СсылкаНаСуществующий = Справочники.Контрагенты.НайтиПоРеквизиту("ИНН", ТекущийИНН);

Если Не СсылкаНаСуществующий.Пустая() Тогда
    // Если нашли — пропускаем итерацию или обновляем данные
    Сообщить("Контрагент с ИНН " + ТекущийИНН + " уже существует. Пропускаем.");
    Продолжить; 
КонецЕсли;

// Если не нашли — создаем новый объект
НовыйОбъект = Справочники.Контрагенты.СоздатьЭлемент();
// Заполнение реквизитов...
НовыйОбъект.Записать();

Оптимизация поиска через запросы

Если предполагается загрузка большого объема данных (тысячи строк), использование НайтиПоРеквизиту() в цикле может существенно замедлить работу системы, так как на каждой итерации будет происходить отдельное обращение к базе данных. Проанализируем более профессиональный подход — использование запросов.

Рассмотрим вариант, когда мы заранее проверяем наличие группы контрагентов. Однако для разовой загрузки или небольших файлов метод НайтиПоРеквизиту() вполне допустим, при условии, что реквизит "ИНН" в конфигураторе помечен как Индексировать. Без индексации поиск по справочнику превратится в полное сканирование таблицы, что недопустимо для производительности.

Комплексное решение задачи загрузки

Объединим все полученные знания в единый алгоритм. Посмотрим на пример кода, который читает файл, проверяет поля на пустоту и ищет дубликаты.


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

// Читаем объект через фабрику XDTO
ДанныеЗагрузки = ФабрикаXDTO.ПрочитатьXML(ЧтениеXML);

Для Каждого ТекущийОбъектXDTO Из ДанныеЗагрузки.Элементы Цикл
    
    // 1. Поиск по уникальному идентификатору (ИНН)
    ИНН_XDTO = "";
    Если ТекущийОбъектXDTO.Свойства().Получить("ИНН") <> Неопределено Тогда
        ИНН_XDTO = СокрЛП(Строка(ТекущийОбъектXDTO.ИНН));
    КонецЕсли;
    
    Если ПустаяСтрока(ИНН_XDTO) Тогда
        Сообщить("В данных отсутствует ИНН, объект пропущен.");
        Продолжить;
    КонецЕсли;
    
    СсылкаНаКонтрагента = Справочники.Контрагенты.НайтиПоРеквизиту("ИНН", ИНН_XDTO);
    
    Если СсылкаНаКонтрагента.Пустая() Тогда
        Объект1С = Справочники.Контрагенты.СоздатьЭлемент();
        Объект1С.ИНН = ИНН_XDTO;
    Иначе
        // Если нужно обновлять существующие — получаем объект
        Объект1С = СсылкаНаКонтрагента.ПолучитьОбъект();
    КонецЕсли;
    
    // 2. Проверка заполненности атрибутов (Адрес, Телефон)
    // Используем вспомогательную логику для каждого поля
    Если ТекущийОбъектXDTO.Свойства().Получить("Адрес") <> Неопределено Тогда
        ЗначениеАдреса = Строка(ТекущийОбъектXDTO.Адрес);
        Если ЗначениеЗаполнено(ЗначениеАдреса) Тогда
            Объект1С.Адрес = ЗначениеАдреса;
        КонецЕсли;
    КонецЕсли;
    
    Если ТекущийОбъектXDTO.Свойства().Получить("Телефоны") <> Неопределено Тогда
        ЗначениеТелефона = Строка(ТекущийОбъектXDTO.Телефоны);
        Если ЗначениеЗаполнено(ЗначениеТелефона) Тогда
            Объект1С.Телефоны = ЗначениеТелефона;
        КонецЕсли;
    КонецЕсли;
    
    // 3. Запись данных
    Попытка
        Объект1С.Записать();
    Исключение
        Сообщить("Ошибка при записи контрагента: " + ОписаниеОшибки());
    КонецПопытки;
    
КонецЦикла;

ЧтениеXML.Закрыть();

Важные советы по работе с данными

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

  1. Режим загрузки: При создании объекта справочника устанавливайте Объект.ОбменДанными.Загрузка = Истина. Это отключит прикладные проверки в модуле объекта, которые могут помешать загрузке технических данных или замедлить процесс.
  2. Транзакции: Если данных много, оберните цикл в транзакцию (НачатьТранзакцию() ... ЗафиксироватьТранзакцию()). Это не только ускорит запись, но и гарантирует целостность данных: если один объект запишется с ошибкой, вы сможете откатить все изменения.
  3. Типизация в XDTO: Если ваш XML-файл привязан к пакету XDTO, платформа автоматически пытается преобразовать данные. Например, дата в XML 2023-10-25 автоматически станет типом Дата в 1С. Всегда проверяйте, какой тип возвращает фабрика, прежде чем использовать строковые функции.
  4. Обработка пустых тегов: В XML есть разница между отсутствующим тегом и тегом с атрибутом xsi:nil="true". Метод Свойство() помогает определить физическое наличие тега, а ЗначениеЗаполнено() — наличие в нем полезной информации.

Таким образом, мы выяснили, что основной проблемой при работе с XDTO является непонимание типов данных. Объекты XDTO требуют внимательного обращения и использования встроенных методов метаданных (Свойства()) для обеспечения стабильной работы обмена данными.

← На главную