При обмене данными с внешними системами программисты 1С часто сталкиваются с форматами XML, где полезная информация содержится не в текстовом содержимом узлов, а в их атрибутах. Особую сложность представляют документы, где множество однотипных узлов имеют одинаковые имена, а их смысл определяется значением одного из атрибутов (например, name="Телефон"). В этой статье мы подробно разберем, как эффективно обработать такой файл, проанализируем ошибки при использовании потокового чтения и научимся применять объектную модель DOM для быстрого извлечения данных.
Рассмотрим структуру XML, с которой возникла задача. Это типичный результат выгрузки формы обратной связи:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE feedback>
<feedback date="2023-10-06T09:37:13.000000Z" id="22">
<feedbackData>
<field name="type" value="Не нашли то что искали"/>
<field name="Имя" value="Василий "/>
<field name="Телефон" value="+7хххххххххх"/>
<field name="E-mail" value="ххххххххххх@gmail.com"/>
<field name="Сообщение" value="Нужна запчасть артикул ххххххххх"/>
</feedbackData>
</feedback>
Проанализируем ситуацию: здесь мы видим корень feedback с атрибутами даты и идентификатора, внутри которого находится контейнер feedbackData. Сами данные хранятся в элементах field. Обратите внимание, что имя поля (ключ) и его содержимое (значение) разнесены по разным атрибутам одного узла. Это стандартная практика для гибких систем, но для простого парсера это создает дополнительные сложности.
Рассмотрим попытку решения задачи через объект ЧтениеXML. Этот объект реализует модель SAX (Simple API for XML) — он читает файл последовательно, узел за узлом, не загружая весь документ в память. Это отлично подходит для гигантских файлов (сотни мегабайт), но усложняет логику обработки небольших документов.
Проблема в том, что при использовании Парсер.Прочитать() на каждом шаге мы видим только текущий "кусочек" XML. Когда парсер находится на узле field, нам нужно прочитать все его атрибуты, сопоставить их и только потом сохранить результат. Если просто выводить Парсер.Имя и Парсер.Значение в цикле Пока Парсер.ПрочитатьАтрибут(), мы получим разрозненный список строк, который трудно связать в единую структуру данных.
Рассмотрим правильный подход при использовании ЧтениеXML:
Парсер = Новый ЧтениеXML;
Парсер.ОткрытьФайл(ПутьКФайлу);
Результат = Новый Структура;
Пока Парсер.Прочитать() Цикл
Если Парсер.ТипУзла = ТипУзлаXML.НачалоЭлемента И Парсер.Имя = "field" Тогда
// Подготавливаем временные переменные для хранения пары Ключ-Значение
ИмяПоля = "";
ЗначениеПоля = "";
// Перебираем все атрибуты текущего узла "field"
Пока Парсер.ПрочитатьАтрибут() Цикл
Если Парсер.Имя = "name" Тогда
ИмяПоля = Парсер.Значение;
ИначеЕсли Парсер.Имя = "value" Тогда
ЗначениеПоля = Парсер.Значение;
КонецЕсли;
КонецЦикла;
// Если ключ найден, записываем в структуру
Если ЗначениеЗаполнено(ИмяПоля) Тогда
Результат.Вставить(СтрЗаменить(ИмяПоля, " ", "_"), ЗначениеПоля);
КонецЕсли;
КонецЕсли;
КонецЦикла;
Парсер.Закрыть();
Выясним причину, почему этот код сложнее: нам приходится вручную следить за именами атрибутов и очищать ключи для структуры (так как в Структура нельзя вставлять ключи с пробелами, например, "E-mail" или "Имя поля").
Для небольших и средних XML-файлов (как в нашем примере) гораздо удобнее использовать ПостроительDOM. Этот объект считывает XML и выстраивает в памяти дерево объектов, к которым можно обращаться произвольно.
Разберем по шагам реализацию через DOM:
ЧтениеXML для доступа к файлу.ПостроительDOM и считываем весь документ в объект ДокументDOM.ПолучитьЭлементыПоИмени() для поиска всех узлов field.ПолучитьАтрибут().Проанализируем пример кода:
Чтение = Новый ЧтениеXML;
Чтение.ОткрытьФайл(ПутьКФайлу);
Построитель = Новый ПостроительDOM;
ДокументDOM = Построитель.Прочитать(Чтение);
// Получаем все узлы с данными
СписокУзлов = ДокументDOM.ПолучитьЭлементыПоИмени("field");
ДанныеФормы = Новый Соответствие;
Для каждого Узел Из СписокУзлов Цикл
Ключ = Узел.Атрибуты.ПолучитьИменнойУзел("name").Значение;
Значение = Узел.Атрибуты.ПолучитьИменнойУзел("value").Значение;
ДанныеФормы.Вставить(Ключ, Значение);
КонецЦикла;
Чтение.Закрыть();
// Теперь мы можем легко обращаться к данным
Сообщить("Имя клиента: " + ДанныеФормы.Получить("Имя"));
Сообщить("Телефон: " + ДанныеФормы.Получить("Телефон"));
В данном случае использование Соответствие вместо Структура позволяет избежать проблем с именами ключей, содержащими спецсимволы или пробелы.
Если нам не нужно перебирать все поля, а требуется получить конкретное значение (например, только телефон), мы можем воспользоваться механизмом XPath в рамках модели DOM. Это позволяет "прицельно" достать нужный узел без циклов.
Посмотрим на пример извлечения телефона:
// Предположим, ДокументDOM уже получен, как в примере выше
Распознаватель = Новый РаспознавательПространствИменDOM(ДокументDOM);
// Ищем атрибут value у элемента field, где атрибут name равен 'Телефон'
РезультатXPath = ДокументDOM.ВычислитьВыражениеXPath("//field[@name='Телефон']/@value", ДокументDOM, Распознаватель, ТипРезультатаDOMXPath.ПервыйУпорядоченныйУзел);
Если РезультатXPath.УзелЗначение <> Неопределено Тогда
Сообщить("Найден телефон: " + РезультатXPath.УзелЗначение.ЗначениеУзла);
КонецЕсли;
Рассмотрим несколько критических моментов, которые часто упускают из виду:
1. Обработка даты в формате ISO 8601. В нашем примере дата указана как 2023-10-06T09:37:13.000000Z. Платформа 1С не всегда корректно понимает миллисекунды и символ "Z" (указание на часовой пояс UTC) при использовании стандартной функции XMLЗначение(Тип("Дата"), ...). Для надежности рекомендуется предварительно обрабатывать строку даты:
СтрДата = "2023-10-06T09:37:13.000000Z";
// Убираем миллисекунды и суффикс Z для приведения к формату 1С
ЧистаяДата = Лев(СтрЗаменить(СтрДата, "T", ""), 14);
// Или через СтрШаблон приводим к ГГГГММДДЧЧММСС
Дата1С = Дата(ЧистаяДата);
2. Спецификация DTD. В начале файла указано <!DOCTYPE feedback>. Если 1С при чтении выдает ошибку, это может означать, что парсер пытается найти внешнюю схему. Чтобы этого избежать, используйте настройки чтения:
ПараметрыЧтения = Новый ПараметрыЧтенияXML;
ПараметрыЧтения.ЗапретитьDTD = Ложь; // Позволяем обрабатывать встроенные определения
Чтение.ОткрытьФайл(ПутьКФайлу, ПараметрыЧтения);
3. Спецсимволы и кодировка. Если в значениях встречаются неразрывные пробелы или символы других алфавитов, всегда проверяйте, что файл открывается в кодировке UTF-8. Если XML передается в виде строки, используйте Чтение.УстановитьСтроку(), это избавит от проблем с временными файлами и кодировками файловой системы.
Мы проанализировали два основных подхода к парсингу XML в 1С. Потоковое чтение (ЧтениеXML) идеально подходит для обработки очень больших файлов с целью экономии оперативной памяти, но требует написания сложной логики по сборке атрибутов. ПостроительDOM — это современный и удобный способ для работы с небольшими документами, который позволяет обращаться к данным как к объектам и использовать мощный язык запросов XPath.
Для большинства задач интеграции, связанных с веб-формами, заказами или небольшими отчетами, использование ПостроительDOM в сочетании с Соответствие является наиболее стабильным и читаемым решением.