При работе с XML-файлами в среде 1С:Предприятие разработчики часто сталкиваются с ситуацией, когда при обходе дерева узлов объект ЧтениеXML возвращает пустое значение для тега, который визуально содержит текст. Например, в строке <price>150.0</price> ожидается получение числа "150.0", но свойство Значение оказывается пустой строкой. В этой статье мы подробно разберем, как устроена потоковая модель чтения XML, почему возникает эта сложность и какими способами можно надежно извлечь данные.
Для начала проанализируем ситуацию с точки зрения объектной модели 1С. Объект ЧтениеXML реализует так называемый "потоковый" (Forward-only) парсер. Он не загружает весь документ в память целиком, а двигается по нему последовательно, узел за узлом.
Важно понимать, что с точки зрения парсера простая конструкция <price>150.0</price> состоит не из одного, а из трех различных узлов:
<price>. Здесь доступно Имя узла ("price"), но свойство Значение всегда будет пустым, так как сам тег не является текстом.</price>.Рассмотрим типичный ошибочный код, с которым часто приходят на форумы:
Если Чтение.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
Если Чтение.Имя = "price" Тогда
// Ошибка: здесь Чтение.Значение всегда ""
Сумма = Сумма + Число(Чтение.Значение);
КонецЕсли;
КонецЕсли;
В данном примере разработчик пытается получить значение в момент нахождения на "заголовке" тега. Программа видит, что это начало элемента "price", но текста там еще нет — он будет на следующем шаге чтения.
Самый простой и очевидный способ исправить ситуацию — принудительно сдвинуть указатель чтения на следующий узел сразу после того, как мы обнаружили нужный нам тег НачалоЭлемента. Рассмотрим, как это выглядит в коде:
Пока Чтение.Прочитать() Цикл
Если Чтение.ТипУзла = ТипУзлаXML.НачалоЭлемента И Чтение.Имя = "price" Тогда
// Переходим от начала тега к его содержимому (узлу Текст)
Чтение.Прочитать();
Сумма = Сумма + Число(Чтение.Значение);
КонецЕсли;
КонецЦикла;
Внимание: Этот метод рабочий, но он "хрупкий". Если XML-файл отформатирован с отступами и переносами строк, между НачалоЭлемента и Текст может оказаться узел типа ИгнорируемыеПробелы. В этом случае однократный вызов Прочитать() может не дойти до данных.
Платформа 1С предоставляет более мощные и безопасные инструменты для извлечения содержимого. Рассмотрим методы ПрочитатьСодержимоеКакСтроку() и ПрочитатьСодержимоеКакЧисло(). Эти методы "умные": они сами считывают текстовый узел и перемещают курсор сразу к закрывающему тегу КонецЭлемента.
Проанализируем пример использования для нашей задачи:
Пока Чтение.Прочитать() Цикл
Если Чтение.ТипУзла = ТипУзлаXML.НачалоЭлемента И Чтение.Имя = "price" Тогда
// Метод сам найдет текст, пропустив пробелы, и вернет число
// Он учитывает стандарт XML (точка как разделитель)
Сумма = Сумма + Чтение.ПрочитатьСодержимоеКакЧисло();
КонецЕсли;
КонецЦикла;
Преимущества этого подхода:
Чтобы избежать проблем с "пустыми" узлами, возникающими из-за красивого форматирования XML (пробелы, табы), рекомендуется правильно инициализировать объект ЧтениеXML. Выясним, как это сделать с помощью параметров.
ПараметрыЧтения = Новый ПараметрыЧтенияXML(,,, ТипПроверкиXML.Нет, Истина);
Чтение = Новый ЧтениеXML;
Чтение.ОткрытьФайл("C:\data\prices.xml", ПараметрыЧтения);
Здесь последний параметр Истина указывает системе, что нужно игнорировать пустые пространства. В этом случае парсер будет "перепрыгивать" через переносы строк и отступы, оставляя в потоке только значимые узлы. Это делает разработку через Прочитать() гораздо стабильнее.
Иногда новички путают содержимое тега и его атрибуты. Разберем разницу на примере: <price currency="RUB">150.0</price>.
В этом случае "150.0" — это значение (текстовый узел), а "RUB" — это атрибут. Чтобы получить атрибут, не нужно двигаться по узлам. Нужно, находясь на узле НачалоЭлемента, вызвать соответствующий метод:
Если Чтение.ТипУзла = ТипУзлаXML.НачалоЭлемента И Чтение.Имя = "price" Тогда
Валюта = Чтение.ПолучитьАтрибут("currency"); // Получим "RUB"
Значение = Чтение.ПрочитатьСодержимоеКакЧисло(); // Получим 150.0
КонецЕсли;
Если структура вашего XML-файла сложная и иерархичная, ручное перебирание узлов через ЧтениеXML может стать слишком трудоемким. В таких случаях стоит рассмотреть механизм XDTO (XML Data Transfer Objects).
Посмотрим на пример, как можно прочитать файл целиком в объектную модель:
Чтение = Новый ЧтениеXML;
Чтение.ОткрытьФайл("C:\data\prices.xml");
// Читаем весь файл в объект
МойОбъект = ФабрикаXDTO.ПрочитатьXML(Чтение);
// Обращаемся к данным как к свойствам объекта
Сумма = Сумма + МойОбъект.price;
Этот способ полностью избавляет от необходимости следить за типами узлов (начало, текст, конец), так как 1С сама преобразует XML в структуру объектов и коллекций.
Для эффективной работы с XML в 1С придерживайтесь следующих правил:
ЧтениеXML — это курсор. Он стоит либо на теге, либо на тексте.ПрочитатьСодержимоеКакСтроку() или аналоги для получения данных внутри тегов.