Работа с XML-файлами — это одна из самых частых задач при интеграции 1С с внешними системами или при загрузке файлов отчетности для ЭДО (например, Книги продаж или Книги покупок). В данной статье мы подробно разберем, как правильно прочитать структуру XML и перенести данные в табличную часть обработки, используя современные механизмы платформы.
Существует два основных подхода к чтению XML в 1С. Рассмотрим их подробнее, чтобы понять, какой выбрать в вашем случае.
ЧтениеXML. Это последовательное чтение файла (SAX-подобный метод). Мы «бежим» по узлам и атрибутам. Этот метод хорош для очень больших файлов, так как он не загружает весь файл в память, но он требует написания большого объема кода для ручного разбора каждого тега.Проанализируем пример использования ФабрикаXDTO, который позволяет быстро получить доступ к данным документа или осуществить визуализацию данных через XSLT.
ХМЛ = Новый ЧтениеXML;
ХМЛ.ОткрытьФайл(ИмяФайла);
// Читаем весь файл в объектную модель XDTO
Данные = ФабрикаXDTO.ПрочитатьXML(ХМЛ);
ХМЛ.Закрыть();
После выполнения этого кода переменная Данные будет содержать всю структуру файла. Если наш XML — это файл книги продаж, мы сможем обращаться к нему по именам узлов: Данные.Документ.СвПродаж.
Одной из типичных проблем является ситуация, когда в XML может быть как одна запись, так и несколько. Если записей много, 1С воспринимает их как СписокXDTO. Если запись одна — как отдельный ОбъектXDTO. Чтобы код не «падал» с ошибкой, нужно правильно организовать цикл.
Рассмотрим, как реализовать универсальный обход записей СвПродаж:
// Проверяем, есть ли данные в узле СвПродаж
Если Данные.Документ.Свойства().Существует("СвПродаж") Тогда
// Получаем значение узла
Записи = Данные.Документ.СвПродаж;
// Если это список (несколько записей), перебираем их
Если ТипЗнч(Записи) = Тип("СписокXDTO") Тогда
Для Каждого ТекущаяЗапись Из Записи Цикл
ОбработатьСтрокуXML(ТекущаяЗапись);
КонецЦикла;
Иначе
// Если это одна запись (объект), обрабатываем только её
ОбработатьСтрокуXML(Записи);
КонецЕсли;
КонецЕсли;
Часто в XML-файлах налоговой отчетности данные контрагента могут находиться в разных атрибутах в зависимости от его типа: ИННЮЛ для юридических лиц или ИННФЛ для физических лиц. Попытка прямого обращения к отсутствующему свойству (например, ОбъектХМЛ.ИННЮЛ, если это физлицо) вызовет программную ошибку.
Разберем по шагам, как правильно проверить наличие свойства и загрузить данные в табличную часть Список нашей обработки:
Для Каждого ОбъектХМЛ Из СписокЗаписей Цикл
НоваяСтрока = Объект.Список.Добавить();
// Заполняем простые реквизиты
НоваяСтрока.НомерСФ = ОбъектХМЛ.НомерСчФ;
// Обработка даты. В XML даты часто хранятся как строки типа "ГГГГ-ММ-ДД"
// Используем XMLЗначение для корректного преобразования
Попытка
НоваяСтрока.ДатаСФ = XMLЗначение(Тип("Дата"), ОбъектХМЛ.ДатаСчФ);
Исключение
НоваяСтрока.ДатаСФ = Дата(1, 1, 1);
КонецПопытки;
// Логика выбора ИНН: проверяем наличие свойства ИННЮЛ
Если ОбъектХМЛ.Свойства().Получить("ИННЮЛ") <> Неопределено Тогда
ТекущийИНН = ОбъектХМЛ.ИННЮЛ;
ИначеЕсли ОбъектХМЛ.Свойства().Получить("ИННФЛ") <> Неопределено Тогда
ТекущийИНН = ОбъектХМЛ.ИННФЛ;
Иначе
ТекущийИНН = "";
КонецЕсли;
НоваяСтрока.ИНН = ТекущийИНН;
// Поиск контрагента в базе по ИНН
Если ЗначениеЗаполнено(ТекущийИНН) Тогда
НоваяСтрока.Контрагент = Справочники.Контрагенты.НайтиПоРеквизиту("ИНН", ТекущийИНН);
КонецЕсли;
// Загрузка суммовых показателей
НоваяСтрока.Сумма = ОбъектХМЛ.СтТовУчНалРубКоп;
НоваяСтрока.СуммаНДС = ОбъектХМЛ.СумНДСРубКоп20;
КонецЦикла;
Выясним причину, почему загрузка больших файлов может занимать много времени. В примере выше мы используем НайтиПоРеквизиту внутри цикла. Если в файле 5 000 строк, система выполнит 5 000 отдельных запросов к базе данных. Это крайне неэффективно.
Проанализируем ситуацию и применим метод кэширования:
Контрагенты, чтобы найти все ссылки по этому списку ИНН.Соответствие, где Ключ — это ИНН, а Значение — Ссылка.Соответствия. Это ускорит загрузку в десятки раз.Пример кода для оптимизации:
// Создаем соответствие для кэша
КэшКонтрагентов = Новый Соответствие;
// Запрос для получения всех контрагентов разом
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Контрагенты.Ссылка КАК Ссылка,
| Контрагенты.ИНН КАК ИНН
|ИЗ
| Справочник.Контрагенты КАК Контрагенты
|ГДЕ
| Контрагенты.ИНН В (&МассивИНН)";
Запрос.УстановитьПараметр("МассивИНН", МассивВсехИННизФайла);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
КэшКонтрагентов.Вставить(Выборка.ИНН, Выборка.Ссылка);
КонецЦикла;
// Теперь в цикле вместо НайтиПоРеквизиту используем:
НоваяСтрока.Контрагент = КэшКонтрагентов.Получить(ТекущийИНН);
Если файл весит сотни мегабайт, метод ФабрикаXDTO.ПрочитатьXML вызовет ошибку нехватки памяти. В этом случае мы рекомендуем комбинированный метод: используем ЧтениеXML, чтобы «дойти» до нужного узла, и читаем только этот узел через XDTO.
Посмотрим на пример такого подхода:
Чтение = Новый ЧтениеXML;
Чтение.ОткрытьФайл(ПутьКФайлу);
// Переходим к началу элементов Документ или СвПродаж
Пока Чтение.Прочитать() Цикл
Если Чтение.ТипУзла = ТипУзлаXML.НачалоЭлемента И Чтение.Имя = "СвПродаж" Тогда
// Читаем только этот кусочек XML как объект XDTO
ТипОбъекта = ФабрикаXDTO.Тип("http://www.v8.1c.ru/ssl/abc", "ТипСвПродаж"); // Укажите ваш тип
ОбъектДанных = ФабрикаXDTO.ПрочитатьXDTO(Чтение);
// Обрабатываем объект и идем дальше, память не перегружается
ОбработатьДанные(ОбъектДанных);
КонецЕсли;
КонецЦикла;
Таким образом, мы объединили простоту обращения через точку (XDTO) и экономичность последовательного чтения. Это решение является наиболее профессиональным при создании универсальных загрузчиков данных.
Важный момент: всегда проверяйте типы данных. Значения из XML приходят как строки. Если ваша колонка в табличной части имеет тип Число, используйте явное преобразование Число(), предварительно заменив точку на запятую, если это требуется локализацией (хотя XMLЗначение обычно справляется с этим автоматически).
Используя данные подходы, вы сможете создать надежный и быстрый механизм загрузки любых XML-данных в ваши обработки 1С.