Как правильно загрузить данные из XML-файла в 1С и избежать типовых ошибок?

Программист 1С v8.3 (Управляемые формы) 1C:Бухгалтерия IT и автоматизация бизнеса
← На главную

Интеграция 1С со сторонними информационными системами через файлы обмена в формате XML — одна из классических задач разработчика. Однако при реализации разбора структуры файла и создании прикладных объектов платформы программисты нередко сталкиваются с рядом характерных ошибок: блокировкой синхронных методов на клиенте, выходом индекса за границы массива при отсутствии тегов, а также некорректным заполнением реквизитов документов и справочников. Разберем по шагам, как выявить эти проблемы и как грамотно построить алгоритм загрузки.

Причина 1. Ошибка «Использование синхронных методов на клиенте запрещено!»

При использовании стандартного диалога выбора файлов в режиме управляемого приложения на клиенте платформа может выдать исключение, блокирующее дальнейшую работу. Рассмотрим подробнее, почему это происходит.

В современных версиях платформы «1С:Предприятие 8.3» по умолчанию установлен запрет на модальные окна и синхронные методы для обеспечения гладкой работы в веб-клиенте и асинхронной среде. Метод ДиалогВыбораФайла.Выбрать() является синхронным.

Решить данную проблему можно двумя способами:

  1. Быстрое решение для разработки или переходного периода: в свойствах конфигурации в дереве метаданных установить свойство Режим использования синхронных вызовов расширений платформы и внешних компонент в значение Использовать.
  2. Архитектурно правильное решение: переписать клиентский код на асинхронные методы (например, использовать асинхронный метод ВыбратьАсинх() или методы работы с файловыми диалогами с оповещением).

Причина 2. Ошибка «Индекс находится за границами массива» при парсинге

Проанализируем фрагмент кода, где значения из файла складываются в плоский СписокЗначений, а затем читаются по жестким индексам:


НовыйЭлемент.Дата = СЗ[0].Значение;
НовыйЭлемент.Код = СЗ[1].Значение;
НовыйЭлемент.Наименование = СЗ[2].Значение;
НовыйЭлемент.Кабинет = СЗ[3].Значение;
НовыйЭлемент.Сумма = СЗ[4].Значение;

Если в XML-файле отсутствует хотя бы один из ожидаемых тегов (например, не передан узел суммы для неоплаченной услуги) или порядок следования узлов изменился, в списке окажется меньше элементов, чем ожидает код. При попытке обращения к СЗ[4] возникает аварийное завершение с ошибкой выхода за границы массива.

Выясним причину и исправим логику. В простейшем варианте для обхода ошибки можно применить конструкцию обработки исключений:


Попытка
    НовыйЭлемент.Сумма = СЗ[4].Значение;
Исключение
    НовыйЭлемент.Сумма = 0;
    Сообщить("В строке не найдена сумма, установлено значение по умолчанию!");
КонецПопытки;

Однако более надежным подходом является отказ от плоских списков с индексами в пользу именованной коллекции Структура. Зададим поля структуры заранее значениями по умолчанию, а при чтении XML будем заполнять их по ключам. Это полностью исключит сдвиг колонок и ошибки границ массива.

Причина 3. Особенности передачи файлов между клиентом и сервером

Обратите внимание на то, как файл передается в серверную процедуру. Передача локального пути клиента (например, C:\upload\data.xml) в процедуру с директивой &НаСервере сработает только в файловой базе на одном ПК. В клиент-серверном варианте сервер попытается найти этот путь на своем диске и выдаст ошибку отсутствия файла.

Правильный путь: поместить файл во временное хранилище на клиенте, передать полученный адрес на сервер, сохранить его во временный файл через ПолучитьИмяВременногоФайла() и только затем передавать путь в ЧтениеXML.ОткрытьФайл(). Кроме того, после завершения работы с файлом объект ЧтениеXML необходимо обязательно закрывать методом Закрыть(), чтобы не блокировать файл в операционной системе.

Причина 4. Некорректное заполнение реквизитов документа

Посмотрим на частую ошибку новичков при заполнении сложных документов вроде ОтчетОРозничныхПродажах: попытка напрямую присвоить строковые значения в ссылочные поля шапки и табличной части.

В типовых конфигурациях действуют жесткие правила:

  1. Табличная часть заполняется через добавление строк: НоваяСтрока = ДокументОбъект.Товары.Добавить(). Нельзя обращаться напрямую к реквизиту табличной части через имя таблицы без указания конкретной строки.
  2. Поля типа «СправочникСсылка» (например, «Номенклатура», «Склад», «Организация») требуют подстановки именно ссылок, а не строк. Перед записью необходимо выполнить поиск существующих элементов (например, через Справочники.Номенклатура.НайтиПоКоду() или НайтиПоНаименованию()).
  3. Документ не запишется, если не заполнены обязательные системные реквизиты: организация, валюта, склад, счета учета.

Правильный пример разбора 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.Закрыть();
    
КонецПроцедуры

&НаСервере
Процедура СоздатьЗаписьСправочника(Данные)
    
    НовыйЭлемент = Справочники.Выгрузка.СоздатьЭлемент();
    НовыйЭлемент.Дата = Данные.Дата;
    НовыйЭлемент.Код = Данные.Код;
    НовыйЭлемент.Наименование = Данные.Наименование;
    НовыйЭлемент.Кабинет = Данные.Кабинет;
    НовыйЭлемент.Сумма = Данные.Сумма;
    
    Попытка
        НовыйЭлемент.Записать();
    Исключение
        Сообщить("Ошибка при записи элемента: " + ОписаниеОшибки());
    КонецПопытки;
    
КонецПроцедуры

Такой подход обеспечивает устойчивость алгоритма к перестановке или отсутствию тегов, гарантирует освобождение дескриптора файла и корректное приведение строковых данных к типам платформы.

← На главную