Как отфильтровать отрицательные значения при разборе XML через XDTO в 1С

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

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

Анализ проблемы и постановка задачи

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

Шаг 1. Настройка последовательного чтения XML

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


ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл(АдресФайла);
ТипПриходныйОрдерXDTO = ФабрикаXDTO.Тип("http://www.wialon.com", "messages"); 
ПоказанияКан = 0;

Здесь важно убедиться, что ФабрикаXDTO правильно определяет пространство имен. Если схема XML заранее не известна, можно использовать чтение без жесткой привязки к типу, однако типизация через ТипXDTO делает код более устойчивым к ошибкам структуры.

Шаг 2. Разбор структуры и обработка коллекции сообщений

Одной из особенностей работы с XDTO является автоматическое преобразование XML-узлов в свойства объектов. Однако, когда мы встречаем узел с вложенными элементами, нам нужно организовать цикл. Выясним причину, по которой стандартный обход может давать сбои. Часто в XML от Wialon корневой узел содержит атрибут count, указывающий на количество записей.

Рассмотрим правильную логику обхода элементов, где мы проверяем каждое сообщение на соответствие нашим критериям:


Пока ЧтениеXML.Прочитать() Цикл
    Если ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента И ЧтениеXML.Имя = "messages" Тогда
        // Десериализация узла в объект XDTO
        ДокументXDTO = ФабрикаXDTO.ПрочитатьXML(ЧтениеXML, ТипПриходныйОрдерXDTO);
        
        // Если в ответе только одно сообщение (структура объекта)
        Если ДокументXDTO.count = "1" Тогда
            Если ДокументXDTO.message.Свойства().Получить("can_mileage") <> Неопределено Тогда
                ЗначениеПробега = Число(ДокументXDTO.message.can_mileage);
                Если ЗначениеПробега > 0 Тогда
                    ПоказанияКан = ЗначениеПробега;
                    Прервать; 
                КонецЕсли;
            КонецЕсли;
        Иначе 
            // Если в ответе список сообщений (итерируемая коллекция)
            Для каждого ТекущееСообщение Из ДокументXDTO.message Цикл
                Если ТекущееСообщение.Свойства().Получить("can_mileage") <> Неопределено Тогда
                    // Важный момент: преобразуем строку в число для сравнения
                    ЗначениеПробега = Число(ТекущееСообщение.can_mileage);
                    Если ЗначениеПробега > 0 Тогда
                        ПоказанияКан = ЗначениеПробега;
                        Прервать; // Нашли первое положительное, выходим из цикла
                    КонецЕсли;
                КонецЕсли;
            КонецЦикла;
        КонецЕсли;
    КонецЕсли;
КонецЦикла;

Шаг 3. Решение проблемы преобразования типов (Точка vs Запятая)

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

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


СтрокаЗначение = ТекущееСообщение.can_mileage;
// Заменяем точку на пустую строку (если это разделитель тысяч) 
// или на неразрывный пробел не нужно, нам нужна запятая для дробной части
СтрокаЗначение = СтрЗаменить(СтрокаЗначение, ".", ЧетвертыйСимволЧисла); // где ЧетвертыйСимвол — разделитель дробной части в 1С
// Но проще использовать универсальный метод:
СтрокаЗначение = СтрЗаменить(СтрокаЗначение, ".", ",");
ПоказанияКанЧислом = 0;
Попытка
    ПоказанияКанЧислом = Число(СтрЗаменить(СтрокаЗначение, Символы.НПП, ""));
Исключение
    ПоказанияКанЧислом = 0;
КонецПопытки;

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

Шаг 4. Обработка неопределенных свойств в XDTO

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

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

Альтернативный подход: Использование DOM и XPath

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

Проанализируем пример такого подхода:


ЧтениеDOM = Новый ЧтениеDOM;
ДокументDOM = ЧтениеDOM.Прочитать(ЧтениеXML);
// XPath запрос: найти message, у которого can_mileage больше 0
ЗапросXPath = "//message[number(can_mileage) > 0]";
РезультатXPath = ДокументDOM.ВычислитьВыражениеXPath(ЗапросXPath, ДокументDOM);
Узел = РезультатXPath.ПолучитьСледующий();
Если Узел <> Неопределено Тогда
    ПоказанияКан = Узел.ТекстовоеСодержимое; // Или дальнейший разбор узла
КонецЕсли;

Хотя этот метод лаконичнее, помните, что ПостроительDOM загружает весь файл в оперативную память. Для файлов размером более 50-100 Мб лучше придерживаться метода с ЧтениеXML и XDTO, описанного в начале статьи.

Итоги и рекомендации

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

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

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

← На главную