Интеграция 1С со сторонними информационными системами через файлы обмена в формате XML — одна из классических задач разработчика. Однако при реализации разбора структуры файла и создании прикладных объектов платформы программисты нередко сталкиваются с рядом характерных ошибок: блокировкой синхронных методов на клиенте, выходом индекса за границы массива при отсутствии тегов, а также некорректным заполнением реквизитов документов и справочников. Разберем по шагам, как выявить эти проблемы и как грамотно построить алгоритм загрузки.
При использовании стандартного диалога выбора файлов в режиме управляемого приложения на клиенте платформа может выдать исключение, блокирующее дальнейшую работу. Рассмотрим подробнее, почему это происходит.
В современных версиях платформы «1С:Предприятие 8.3» по умолчанию установлен запрет на модальные окна и синхронные методы для обеспечения гладкой работы в веб-клиенте и асинхронной среде. Метод ДиалогВыбораФайла.Выбрать() является синхронным.
Решить данную проблему можно двумя способами:
Использовать.ВыбратьАсинх() или методы работы с файловыми диалогами с оповещением).Проанализируем фрагмент кода, где значения из файла складываются в плоский СписокЗначений, а затем читаются по жестким индексам:
НовыйЭлемент.Дата = СЗ[0].Значение;
НовыйЭлемент.Код = СЗ[1].Значение;
НовыйЭлемент.Наименование = СЗ[2].Значение;
НовыйЭлемент.Кабинет = СЗ[3].Значение;
НовыйЭлемент.Сумма = СЗ[4].Значение;
Если в XML-файле отсутствует хотя бы один из ожидаемых тегов (например, не передан узел суммы для неоплаченной услуги) или порядок следования узлов изменился, в списке окажется меньше элементов, чем ожидает код. При попытке обращения к СЗ[4] возникает аварийное завершение с ошибкой выхода за границы массива.
Выясним причину и исправим логику. В простейшем варианте для обхода ошибки можно применить конструкцию обработки исключений:
Попытка
НовыйЭлемент.Сумма = СЗ[4].Значение;
Исключение
НовыйЭлемент.Сумма = 0;
Сообщить("В строке не найдена сумма, установлено значение по умолчанию!");
КонецПопытки;
Однако более надежным подходом является отказ от плоских списков с индексами в пользу именованной коллекции Структура. Зададим поля структуры заранее значениями по умолчанию, а при чтении XML будем заполнять их по ключам. Это полностью исключит сдвиг колонок и ошибки границ массива.
Обратите внимание на то, как файл передается в серверную процедуру. Передача локального пути клиента (например, C:\upload\data.xml) в процедуру с директивой &НаСервере сработает только в файловой базе на одном ПК. В клиент-серверном варианте сервер попытается найти этот путь на своем диске и выдаст ошибку отсутствия файла.
Правильный путь: поместить файл во временное хранилище на клиенте, передать полученный адрес на сервер, сохранить его во временный файл через ПолучитьИмяВременногоФайла() и только затем передавать путь в ЧтениеXML.ОткрытьФайл(). Кроме того, после завершения работы с файлом объект ЧтениеXML необходимо обязательно закрывать методом Закрыть(), чтобы не блокировать файл в операционной системе.
Посмотрим на частую ошибку новичков при заполнении сложных документов вроде ОтчетОРозничныхПродажах: попытка напрямую присвоить строковые значения в ссылочные поля шапки и табличной части.
В типовых конфигурациях действуют жесткие правила:
НоваяСтрока = ДокументОбъект.Товары.Добавить(). Нельзя обращаться напрямую к реквизиту табличной части через имя таблицы без указания конкретной строки.Справочники.Номенклатура.НайтиПоКоду() или НайтиПоНаименованию()).Объединим лучшие практики и посмотрим на правильную реализацию разбора узлов XML без риска падения по индексам:
&НаСервере
Процедура ЗагрузитьДанныеИзXML(ПутьКФайлу)
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл(ПутьКФайлу);
ДанныеСтроки = Новый Структура("Дата,Код,Наименование,Кабинет,Сумма", Неопределено, "", "", "", 0);
ТекущийУзел = "";
Пока ЧтениеXML.Прочитать() Цикл
ТипУзла = ЧтениеXML.ТипУзла;
Если ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
ТекущийУзел = ЧтениеXML.Имя;
ИначеЕсли ТипУзла = ТипУзлаXML.Текст Тогда
ЗначениеСтроки = ЧтениеXML.Значение;
Если ВРег(ТекущийУзел) = "VISIT_DATE" Тогда
// Преобразуем строку XML в тип Дата
ДанныеСтроки.Дата = XMLЗначение(Тип("Дата"), ЗначениеСтроки);
ИначеЕсли ВРег(ТекущийУзел) = "SERVICE_CODE" Тогда
ДанныеСтроки.Код = ЗначениеСтроки;
ИначеЕсли ВРег(ТекущийУзел) = "SERVICE_NAME" Тогда
ДанныеСтроки.Наименование = ЗначениеСтроки;
ИначеЕсли ВРег(ТекущийУзел) = "CABLAB_NAME" Тогда
ДанныеСтроки.Кабинет = ЗначениеСтроки;
ИначеЕсли ВРег(ТекущийУзел) = "SUMM_WITH_DISCOUNT" Тогда
ДанныеСтроки.Сумма = XMLЗначение(Тип("Число"), ЗначениеСтроки);
КонецЕсли;
ИначеЕсли ТипУзла = ТипУзлаXML.КонецЭлемента Тогда
// При закрытии узла элемента создаем запись
Если ВРег(ЧтениеXML.Имя) = "STATUS_MNEMO" Тогда
СоздатьЗаписьСправочника(ДанныеСтроки);
// Сбрасываем значения на значения по умолчанию
ДанныеСтроки.Вставить("Дата", Неопределено);
ДанныеСтроки.Вставить("Код", "");
ДанныеСтроки.Вставить("Наименование", "");
ДанныеСтроки.Вставить("Кабинет", "");
ДанныеСтроки.Вставить("Сумма", 0);
КонецЕсли;
КонецЕсли;
КонецЦикла;
ЧтениеXML.Закрыть();
КонецПроцедуры
&НаСервере
Процедура СоздатьЗаписьСправочника(Данные)
НовыйЭлемент = Справочники.Выгрузка.СоздатьЭлемент();
НовыйЭлемент.Дата = Данные.Дата;
НовыйЭлемент.Код = Данные.Код;
НовыйЭлемент.Наименование = Данные.Наименование;
НовыйЭлемент.Кабинет = Данные.Кабинет;
НовыйЭлемент.Сумма = Данные.Сумма;
Попытка
НовыйЭлемент.Записать();
Исключение
Сообщить("Ошибка при записи элемента: " + ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
Такой подход обеспечивает устойчивость алгоритма к перестановке или отсутствию тегов, гарантирует освобождение дескриптора файла и корректное приведение строковых данных к типам платформы.