Работа с XML-файлами — одна из наиболее частотных задач при организации обмена данными в 1С. Типичный сценарий: пользователю необходимо загрузить файл, увидеть список документов в первой таблице, а при выборе конкретной строки — просмотреть детальные свойства этого документа во второй таблице. Рассмотрим подробно, как решить эту задачу, какие инструменты использовать и как избежать типичных ошибок новичков, связанных с производительностью и позиционированием в файле.
Основная сложность, с которой сталкиваются разработчики, заключается в потере связи между строкой в таблице на форме (которая является лишь визуальным представлением данных) и конкретным узлом в XML-структуре. Если в файле встречаются документы с одинаковыми именами, обычный поиск по текстовому значению становится невозможным. Проанализируем ситуацию: для эффективной реализации нам необходимо не только прочитать файл, но и сохранить «ключ», по которому мы сможем мгновенно найти нужные данные при клике пользователя.
Важное правило: Обработку XML-файлов всегда следует выносить на сторону сервера. Объекты ЧтениеXML, ДокументDOM и ОбъектXDTO не являются сериализуемыми, то есть их нельзя передать с сервера на клиент. На клиенте мы лишь инициируем процесс и передаем адрес файла во временном хранилище.
Метод объектной модели документа (DOM) удобен тем, что позволяет загрузить весь файл в память и обращаться к любому узлу по его индексу или через XPath-запросы. Рассмотрим, как правильно связать таблицу формы и структуру XML.
Для начала создадим на форме две таблицы: ТЗ_Документы и ТЗ_Свойства. В таблицу ТЗ_Документы обязательно добавим скрытую колонку ИндексУзла (тип: Число).
Разберем процедуру загрузки первого списка документов:
&НаСервере
Процедура ЗагрузитьСписокДокументовНаСервере(АдресФайла)
ЧтениеXML = Новый ЧтениеXML;
// Получаем файл из временного хранилища, куда он был помещен на клиенте
ИмяФайла = ПолучитьИмяВременногоФайла("xml");
ДвоичныеДанные = ПолучитьИзВременногоХранилища(АдресФайла);
ДвоичныеДанные.Записать(ИмяФайла);
ЧтениеXML.ОткрытьФайл(ИмяФайла);
ПостроительDOM = Новый ПостроительDOM;
ДокументDOM = ПостроительDOM.Прочитать(ЧтениеXML);
ТЗ_Документы.Очистить();
// Перебираем корневые элементы (например, узлы "Объект")
ВсеУзлы = ДокументDOM.ЭлементДокумента.ДочерниеУзлы;
Для Индекс = 0 По ВсеУзлы.Количество() - 1 Цикл
ТекущийУзел = ВсеУзлы.Получить(Индекс);
Если ТекущийУзел.ИмяУзла = "Объект" Тогда
НоваяСтрока = ТЗ_Документы.Добавить();
НоваяСтрока.Наименование = ТекущийУзел.Атрибуты.ПолучитьИменованныйЭлемент("Тип").Значение;
// Сохраняем индекс, чтобы потом быстро найти узел в дереве DOM
НоваяСтрока.ИндексУзла = Индекс;
КонецЕсли;
КонецЦикла;
ЧтениеXML.Закрыть();
УдалитьФайлы(ИмяФайла);
КонецПроцедуры
Теперь, когда пользователь выбирает строку в первой таблице, мы используем событие ПриАктивизацииСтроки или Выбор. В обработчике мы получаем значение ИндексУзла и по нему извлекаем подчиненные свойства.
Если структура XML сложная и многоуровневая, лучше воспользоваться механизмом XDTO. Он позволяет работать с XML как с набором объектов 1С. Рассмотрим рекурсивный метод обхода свойств, который позволяет вывести все вложенные данные документа.
Проанализируем пример кода, который преобразует XML-объект в дерево или таблицу свойств:
&НаСервере
Процедура ПрочитатьСвойстваОбъекта(ОбъектXDTO, РодительскаяСтрока = Неопределено)
// Если объект является списком (коллекцией)
Если ТипЗнч(ОбъектXDTO) = Тип("СписокXDTO") Тогда
Для Каждого ТекущийЭлемент Из ОбъектXDTO Цикл
НоваяСтр = ТЗ_Свойства.Добавить();
НоваяСтр.Имя = ОбъектXDTO.ВладеющееСвойство.Имя;
Если ТипЗнч(ТекущийЭлемент) = Тип("ОбъектXDTO") Тогда
ПрочитатьСвойстваОбъекта(ТекущийЭлемент, НоваяСтр);
Иначе
НоваяСтр.Значение = ТекущийЭлемент;
КонецЕсли;
КонецЦикла;
Иначе
// Перебор свойств одиночного объекта
Для Каждого Свойство Из ОбъектXDTO.Свойства() Цикл
ЗначениеСвойства = ОбъектXDTO.Получить(Свойство);
НоваяСтр = ТЗ_Свойства.Добавить();
НоваяСтр.Имя = Свойство.Имя;
Если ТипЗнч(ЗначениеСвойства) = Тип("ОбъектXDTO") ИЛИ ТипЗнч(ЗначениеСвойства) = Тип("СписокXDTO") Тогда
ПрочитатьСвойстваОбъекта(ЗначениеСвойства, НоваяСтр);
Иначе
НоваяСтр.Значение = ЗначениеСвойства;
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецПроцедуры
Этот метод хорош тем, что он «всеяден». Ему не важно, как называются теги внутри документа, он просто обходит всю структуру ОбъектXDTO.Свойства() и выводит их в таблицу. Это идеальное решение для универсальных инструментов просмотра XML.
Выясним причину, по которой код может не находить узлы в некоторых файлах. Часто XML содержит пространства имен (Namespace), например, xmlns:v8="http://v8.1c.ru/data". В этом случае обычное сравнение Узел.ИмяУзла = "Объект" может вернуть Ложь, так как полное имя узла будет содержать префикс.
Рассмотрим использование XPath. Это язык запросов к XML, который позволяет найти узел по условию без циклов. Чтобы XPath работал корректно с DOM, нужно использовать РаспознавательПространствИменDOM.
Распознаватель = Новый РаспознавательПространствИменDOM(ДокументDOM);
РезультатЗапроса = ДокументDOM.ВычислитьВыражениеXPath("//Объект[@Тип='СправочникСсылка.Номенклатура']",
ДокументDOM, Распознаватель, ТипРезультатаDOMXPath.УпорядоченныйИтераторУзлов);
Использование XPath значительно ускоряет разработку, позволяя писать короткие и понятные запросы к данным вместо вложенных циклов Для Каждого.
Разберем ситуацию с большими файлами. Если ваш XML-файл обмена весит более 100-200 Мб, использование ПостроительDOM или ФабрикаXDTO.ПрочитатьXML() может привести к ошибке "Недостаточно памяти". Это происходит потому, что данные методы загружают всю структуру файла в оперативную память в виде объектов.
В таких случаях рекомендуется использовать последовательное чтение:
ЧтениеXML.Прочитать() в цикле Пока ЧтениеXML.Прочитать() Цикл.Проанализировав вышеописанные подходы, мы видим, что для небольших файлов и удобства разработки лучше всего подходит XDTO или DOM с индексацией, а для высоконагруженных систем — последовательное чтение. Выбор метода зависит от ваших конкретных бизнес-требований и объема обрабатываемых данных.