Работа с XML-файлами — одна из самых частых задач при организации обмена данными между 1С и внешними системами, будь то государственные порталы (Честный Знак, ЕГАИС) или интеграции с сайтами. Одной из наиболее гибких технологий для этого в платформе 1С является Объектная Модель Документа (DOM). Однако при её использовании новички часто сталкиваются с неочевидным поведением системы: изменением заголовков пространств имен, дублированием узлов или некорректным сохранением структуры.
В этой статье мы подробно разберем, как правильно изменять атрибуты, добавлять новые уровни вложенности в существующий XML и почему не стоит пугаться автоматической смены префиксов xmlns.
Рассмотрим стандартную задачу: нам необходимо найти в XML-файле определенный элемент и изменить значение его атрибута. Например, в файле отчетности нужно заменить идентификатор файла ИдФайл. Многие пытаются сделать это простым текстовым поиском СтрЗаменить, но это небезопасно, так как искомая строка может встретиться в данных, а не в служебных полях.
Правильный подход заключается в использовании объектов ЧтениеXML и ПостроительDOM. Давайте разберем этот процесс по шагам:
// 1. Читаем файл в объектную модель
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл(ИмяФайла);
ПостроительDOM = Новый ПостроительDOM;
ДокументDOM = ПостроительDOM.Прочитать(ЧтениеXML);
ЧтениеXML.Закрыть();
// 2. Ищем нужные элементы по имени тега
СписокЭлементов = ДокументDOM.ПолучитьЭлементыПоИмени("Файл");
Для Каждого ЭлементDOM Из СписокЭлементов Цикл
Если ЭлементDOM.ЕстьАтрибут("ИдФайл") Тогда
СтароеЗначение = ЭлементDOM.ПолучитьАтрибут("ИдФайл");
НовоеЗначение = СтрЗаменить(СтароеЗначение, "_0_0_0", "_0_1_0");
// Устанавливаем новое значение атрибута
ЭлементDOM.УстановитьАтрибут("ИдФайл", НовоеЗначение);
КонецЕсли;
КонецЦикла;
Важный нюанс: при записи обновленного документа через ЗаписьXML платформа может автоматически переупорядочить пространства имен (namespaces). Если вы заметили, что вместо xmlns:xsi появилось xmlns:xsd или префиксы поменялись местами — это нормальное поведение парсера. Согласно спецификации XML, порядок атрибутов xmlns и выбор конкретного префикса (если адрес URI совпадает) не влияют на валидность документа.
Часто при работе с маркируемыми товарами требуется добавить в существующий блок ДопСведТов новые вложенные теги, например НомСредИдентТов, внутри которого должны располагаться элементы КИЗ.
Типичная ошибка начинающих — пытаться добавить КИЗ как атрибут. Помните: имена атрибутов внутри одного тега должны быть уникальны. Если вам нужно передать список кодов маркировки, это должны быть именно вложенные элементы (теги), а не атрибуты.
Проанализируем правильный алгоритм добавления вложенных узлов:
ДопСведТов).СоздатьЭлемент.ДобавитьДочерний.Посмотрим на пример реализации, который решает проблему дублирования данных:
// Предположим, мы уже находимся в цикле перебора элементов "ДопСведТов"
Для Каждого Элемент_ДопСведТов Из СписокЭлементов_ДопСведТов Цикл
// Создаем контейнер для кодов маркировки
УзелНомСред = ДокументDOM.СоздатьЭлемент("НомСредИдентТов");
Элемент_ДопСведТов.ДобавитьДочерний(УзелНомСред);
// Массив кодов, которые нужно добавить
МассивКИЗ = ПолучитьСписокКИЗДляТовара();
Для Каждого ТекКод Из МассивКИЗ Цикл
// Создаем элемент КИЗ
УзелКИЗ = ДокументDOM.СоздатьЭлемент("КИЗ");
// Устанавливаем значение внутри тега <КИЗ>Значение</КИЗ>
УзелКИЗ.ТекстовоеСодержимое = ТекКод;
// Добавляем КИЗ в родительский узел НомСредИдентТов
УзелНомСред.ДобавитьДочерний(УзелКИЗ);
КонецЦикла;
КонецЦикла;
Почему нельзя использовать поиск элементов внутри цикла? Встречается ошибка, когда программист после ДобавитьДочерний(НомСредИдентТов) снова вызывает ДокументDOM.ПолучитьЭлементыПоИмени("НомСредИдентТов"). В этом случае поиск вернет все такие элементы в документе, и вы начнете добавлять коды маркировки первого товара во все последующие. Всегда используйте прямую ссылку на созданный объект, которую возвращает метод СоздатьЭлемент.
После внесения всех изменений документ необходимо правильно сохранить. При работе с государственными системами крайне важно соблюдать кодировку (чаще всего это windows-1251). Если использовать настройки по умолчанию, 1С может сохранить файл в UTF-8, что приведет к ошибке при загрузке на портал.
Рассмотрим правильный блок записи:
ЗаписьXML = Новый ЗаписьXML;
// Обязательно указываем кодировку и версию XML
ЗаписьXML.ОткрытьФайл(ИмяФайла, "windows-1251");
ЗаписьXML.ЗаписатьОбъявлениеXML();
ЗаписьDOM = Новый ЗаписьDOM;
ЗаписьDOM.Записать(ДокументDOM, ЗаписьXML);
ЗаписьXML.Закрыть();
Проанализируем ситуацию, когда «обрезается адрес» в пространстве имен. Это может происходить, если вы открыли файл в одной кодировке, а сохраняете в другой, или если в URI содержатся спецсимволы, которые не были экранированы. Используя ЗаписьDOM, мы доверяем платформе 1С формирование корректного XML-синтаксиса, что гораздо надежнее ручной сборки строки.
Выясним причину, по которой опытные разработчики предостерегают от использования СтрЗаменить для правки XML:
xmlns.& или < на соответствующие сущности (& и <). При текстовой вставке вам придется делать это вручную.Однако у DOM есть и минус: он загружает весь файл в оперативную память. Если ваш XML весит сотни мегабайт, лучше использовать последовательную запись через ЧтениеXML и ЗаписьXML (потоковый метод), копируя узлы по одному и модифицируя только нужные. Для такой потоковой корректировки можно использовать обработку для замены данных в XML-файле через перезапись.
Подводя итог, можно сказать, что для большинства задач по модификации небольших и средних XML-файлов (УПД, накладные, отчеты) связка ПостроительDOM + ЗаписьDOM является оптимальным инструментом, обеспечивающим чистоту кода и корректность итогового файла. Если же требуется внести правки вручную в интерактивном режиме, можно применить инструмент для визуального чтения и редактирования XML-файлов.