Как безопасно проверить существование поля в объекте XDTO при работе с XML-файлами в 1С?

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

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

Исходная проблема, с которой мы столкнулись, выглядит следующим образом:


ЭтоКолекция = ?(ТипЗнч(тОбъектОбмена.ТабличнаяЧасть[1].Запись) = Тип("ОбъектXDTO")

В этой строке кода происходит попытка прямого обращения к полю Запись объекта, находящегося в табличной части XDTO. Если в элементе тОбъектОбмена.ТабличнаяЧасть[1] поле Запись отсутствует, система выдаст ошибку. Наша задача — научиться заранее проверять наличие этого поля.

Определение типа элемента XDTO

Прежде чем углубляться в проверку свойств, давайте сначала убедимся, что мы работаем с объектом XDTO. В нашем случае, вопрос возник после получения информации, что ТипЗнч(тОбъектОбмена.ТабличнаяЧасть[1]) является ОбъектXDTO. Это критически важно, так как методы, которые мы будем использовать, применимы именно к этому типу объектов.

ТипЗнч(тОбъектОбмена.ТабличнаяЧасть[1]) // Должно быть ОбъектXDTO

Если бы это был другой тип, например, примитивный тип или СписокXDTO, подходы к доступу к данным могли бы отличаться.

Метод 1: Использование Свойства.Получить(ИмяСвойства) для проверки существования поля

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

Как это работает:

  1. Мы обращаемся к коллекции Свойства объекта XDTO.
  2. Вызываем метод Получить(), передавая ему имя интересующего нас свойства в виде строки.
  3. Метод возвращает объект типа СвойствоXDTO, если свойство с таким именем найдено в схеме объекта XDTO.
  4. Если свойство с указанным именем не обнаружено, метод возвращает значение Неопределено.

Таким образом, мы можем безопасно проверить наличие свойства, прежде чем пытаться получить его значение.

Пример использования Свойства.Получить():


ИмяСвойства = "Запись";
ОбъектТЧ = тОбъектОбмена.ТабличнаяЧасть[1]; // Допустим, это ОбъектXDTO

// Шаг 1: Получаем информацию о свойстве
СвойствоЗапись = ОбъектТЧ.Свойства.Получить(ИмяСвойства);

// Шаг 2: Проверяем, существует ли свойство
Если СвойствоЗапись <> Неопределено Тогда
    // Свойство "Запись" существует, теперь мы можем безопасно получить его значение
    ЗначениеЗаписи = ОбъектТЧ.Получить(ИмяСвойства);
    
    // Дополнительная проверка на тип, если необходимо
    Если ТипЗнч(ЗначениеЗаписи) = Тип("ОбъектXDTO") Тогда
        // Продолжаем работать со ЗначениеЗаписи как с ОбъектXDTO
        Сообщить("Свойство " + ИмяСвойства + " существует и является ОбъектXDTO.");
    Иначе
        Сообщить("Свойство " + ИмяСвойства + " существует, но имеет другой тип: " + ТипЗнч(ЗначениеЗаписи));
    КонецЕсли;
Иначе
    // Свойство "Запись" отсутствует в данном элементе XDTO
    Сообщить("Свойство " + ИмяСвойства + " не найдено в элементе табличной части.");
    ЗначениеЗаписи = Неопределено;
КонецЕсли;

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

Метод 2: Использование Установлено(Свойство) для проверки наличия и заполненности свойства

В дополнение к проверке существования свойства с помощью Свойства.Получить(), мы можем также использовать метод Установлено(). Этот метод позволяет нам не только убедиться, что свойство существует в схеме XDTO, но и что оно имеет какое-либо значение, отличное от стандартного значения по умолчанию (например, null или Неопределено в контексте XDTO).

Как это работает:

  1. Сначала мы получаем объект СвойствоXDTO с помощью Свойства.Получить().
  2. Затем мы передаем этот объект СвойствоXDTO в метод Установлено(), вызванный у нашего основного объекта XDTO.
  3. Метод Установлено() вернет Истина, если свойство существует и имеет установленное значение, и Ложь в противном случае.

Это бывает полезно, когда нам нужно знать не просто о наличии поля, но и о том, что оно действительно было заполнено в XML-файле.

Пример использования Установлено():


ИмяСвойства = "Запись";
ОбъектТЧ = тОбъектОбмена.ТабличнаяЧасть[1]; // Допустим, это ОбъектXDTO

// Шаг 1: Получаем объект СвойствоXDTO
СвойствоДляПроверки = ОбъектТЧ.Свойства.Получить(ИмяСвойства);

// Шаг 2: Проверяем существование и установленное значение
Если СвойствоДляПроверки <> Неопределено И ОбъектТЧ.Установлено(СвойствоДляПроверки) Тогда
    // Свойство "Запись" существует и имеет установленное значение
    ЗначениеЗаписи = ОбъектТЧ.Получить(ИмяСвойства);
    
    Если ТипЗнч(ЗначениеЗаписи) = Тип("ОбъектXDTO") Тогда
        Сообщить("Свойство " + ИмяСвойства + " существует, заполнено и является ОбъектXDTO.");
        // Обработка как ОбъектXDTO
    Иначе
        Сообщить("Свойство " + ИмяСвойства + " существует, заполнено, но имеет другой тип: " + ТипЗнч(ЗначениеЗаписи));
        // Обработка другого типа
    КонецЕсли;
Иначе
    // Свойство "Запись" либо не существует, либо не имеет установленного значения
    Сообщить("Свойство " + ИмяСвойства + " не найдено или не заполнено.");
КонецЕсли;

Использование Установлено() добавляет дополнительный уровень контроля, позволяя нам не только проверять наличие поля, но и его содержательность.

Метод 3: Использование прямого метода Получить("ИмяСвойства") у объекта XDTO

Объект XDTO также имеет прямой метод Получить(), который позволяет получить значение свойства по его имени. Этот метод может быть удобен, но имеет свои особенности, которые мы должны учитывать.

Как это работает:

  1. Мы вызываем метод Получить() непосредственно у объекта XDTO, передавая имя свойства в виде строки.
  2. Если свойство с таким именем присутствует и имеет значение, метод вернет это значение.
  3. Если свойство отсутствует, поведение может варьироваться:
    • В некоторых случаях (особенно для необязательных свойств), он может вернуть Неопределено.
    • В других случаях (например, если свойство является обязательным по схеме, но отсутствует в XML, или при определенных обстоятельствах), он может вызвать ошибку "Ошибка при вызове контекста метода (Получить)" или "Поле объекта не обнаружено".

В нашей исходной проблеме было замечено, что тОбъектОбмена.ТабличнаяЧасть[1].Получить("Запись") вызывал ошибку, в то время как тОбъектОбмена.ТабличнаяЧасть[2].Получить("Запись") успешно возвращал ОбъектXDTO. Это подчеркивает, что поведение метода Получить() может зависеть от конкретной структуры XDTO и того, является ли свойство по схеме обязательным или опциональным, а также от наличия самого элемента. Поэтому, полагаться исключительно на Получить() без предварительной проверки может быть рискованно.

Пример и предостережение:


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

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

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

Работа с табличными частями XDTO (СписокXDTO)

В нашем случае, тОбъектОбмена.ТабличнаяЧасть, скорее всего, является СписокXDTO. При работе с такими коллекциями полезно итерировать по их элементам и проверять каждый элемент по отдельности, так как их структура может быть неоднородной.

Пример итерации по СписокXDTO:


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

Этот подход обеспечивает максимальную гибкость и безопасность при работе с динамически изменяющимися структурами XML.

Общие рекомендации и лучшие практики

  1. Проверяйте родительские объекты: Всегда убеждайтесь, что родительский объект, к которому вы обращаетесь, не является Неопределено. Например, если тОбъектОбмена или тОбъектОбмена.ТабличнаяЧасть могут быть Неопределено, проверяйте это до того, как пытаться обращаться к их элементам или свойствам.
  2. Используйте схему XDTO: Понимание схемы XDTO, на основе которой формируется XML-файл, критически важно. Схема явно определяет, какие поля являются обязательными, а какие — опциональными, и какие типы данных они должны содержать.
  3. Последовательность проверок: Для максимальной надежности, мы рекомендуем следующий порядок действий:
    1. Проверить, что родительский объект не Неопределено.
    2. Если это коллекция (например, СписокXDTO), итерировать по ее элементам.
    3. Для каждого элемента (если он является ОбъектXDTO), использовать Свойства.Получить() для проверки наличия нужного свойства.
    4. При необходимости, использовать Установлено() для проверки, что свойство имеет непустое значение.
    5. Только после всех проверок безопасно получать значение свойства.

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

← На главную