В процессе интеграции 1С с внешними системами (например, системами спутникового мониторинга типа Wialon) разработчики часто сталкиваются с необходимостью корректного разбора XML-ответов. Одной из специфических проблем является получение некорректных или «мусорных» данных, таких как отрицательные значения пробега или температуры, которые могут служить системными флагами отсутствия связи с датчиком. В данной статье мы разберем, как правильно использовать объект ЧтениеXML и механизмы XDTO для фильтрации таких данных.
Рассмотрим ситуацию: при разгрузке данных мониторинга в теге can_mileage (пробег по CAN-шине) приходят значения. Иногда система возвращает отрицательное число (например, -1), которое 1С воспринимает как фактические показания. Наша задача — проигнорировать такие сообщения и найти первое положительное значение в массиве переданных данных. Основная сложность заключается в том, что структура XML может меняться: если в ответе одно сообщение, XDTO представляет его как объект; если сообщений много — как список (коллекцию).
Для начала разберем базовый механизм открытия файла. Использование объекта ЧтениеXML позволяет эффективно обрабатывать даже файлы большого объема, так как чтение происходит последовательно без загрузки всего документа в память. Проанализируем начальный фрагмент кода:
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл(АдресФайла);
ТипПриходныйОрдерXDTO = ФабрикаXDTO.Тип("http://www.wialon.com", "messages");
ПоказанияКан = 0;
Здесь важно убедиться, что ФабрикаXDTO правильно определяет пространство имен. Если схема XML заранее не известна, можно использовать чтение без жесткой привязки к типу, однако типизация через ТипXDTO делает код более устойчивым к ошибкам структуры.
Одной из особенностей работы с 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 Тогда
ПоказанияКан = ЗначениеПробега;
Прервать; // Нашли первое положительное, выходим из цикла
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Проанализируем критическую ошибку, которая часто возникает при выполнении функции Число(): «Преобразование значения к типу Число не может быть выполнено». Это происходит из-за того, что в XML-стандарте разделителем дробной части является точка (например, 1250.45), а в настройках операционной системы и 1С (для русской локализации) ожидается запятая.
Для предотвращения этой ошибки нам необходимо выполнить предварительную очистку строки. Посмотрим на пример безопасного преобразования:
СтрокаЗначение = ТекущееСообщение.can_mileage;
// Заменяем точку на пустую строку (если это разделитель тысяч)
// или на неразрывный пробел не нужно, нам нужна запятая для дробной части
СтрокаЗначение = СтрЗаменить(СтрокаЗначение, ".", ЧетвертыйСимволЧисла); // где ЧетвертыйСимвол — разделитель дробной части в 1С
// Но проще использовать универсальный метод:
СтрокаЗначение = СтрЗаменить(СтрокаЗначение, ".", ",");
ПоказанияКанЧислом = 0;
Попытка
ПоказанияКанЧислом = Число(СтрЗаменить(СтрокаЗначение, Символы.НПП, ""));
Исключение
ПоказанияКанЧислом = 0;
КонецПопытки;
Важный нюанс: при работе с большими числами в строке могут присутствовать неразрывные пробелы (Символы.НПП). Их также рекомендуется удалять перед конвертацией.
В процессе разбора может выясниться, что в некоторых узлах message тег can_mileage вовсе отсутствует. Попытка прямого обращения к ТекущееСообщение.can_mileage вызовет ошибку «Поле объекта не обнаружено». Чтобы этого избежать, используем метод Свойства().Получить().
Рассмотрим, как это работает: объект XDTO имеет скрытую коллекцию свойств. Метод Свойства() возвращает описание всех доступных полей для данной сущности. Если мы передаем в Получить() имя тега и получаем Неопределено, значит, данных в этом пакете нет, и мы можем смело переходить к следующему итератору цикла Для каждого.
Если файл имеет сложную вложенность и вам нужно найти только первое вхождение положительного значения, не перебирая все дерево вручную, можно воспользоваться моделью 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С следует придерживаться следующих правил:
XDTO перед обращением к нему с помощью метода Свойства().Получить().Число(Значение) > 0, чтобы отсечь системные ошибки внешних сервисов (значения -1, 0 или пустые строки).Прервать, чтобы сэкономить ресурсы процессора и не обходить оставшиеся тысячи строк в XML-файле.Следуя этим шагам, вы создадите отказоустойчивый механизм импорта данных, который не будет «падать» при изменении формата ответа сервера или при появлении некорректных показаний датчиков.