При выгрузке структурированных данных (например, электронных счетов-фактур, актов, накладных или отчетности для контролирующих органов) крайне важно гарантировать корректность сформированного XML-файла еще до отправки контрагенту или оператору электронного документооборота. В современных версиях платформы предусмотрены встроенные средства работы со схемами, однако в платформе 1С 7.7 штатные средства валидации по XSD отсутствуют. Разберем, как реализовать надежную программную проверку XML по схеме XSD средствами встроенного языка 1С 7.7.
Для решения задачи валидации в среде 1С 7.7 задействуем системные COM-объекты Microsoft XML Core Services (MSXML). Процесс проверки строится на взаимодействии двух ключевых объектов:
MSXML2.XMLSchemaCache.6.0 — кэш схем, в который загружается целевой файл XSD.MSXML2.DOMDocument.6.0 — объектное представление XML-документа, поддерживающее синтаксический анализ и валидацию по прикрепленному кэшу схем.Рассмотрим структуру и логику создания функции проверки.
Напишем универсальную функцию, которая принимает путь к проверяемому файлу, путь к схеме и количество выводимых сообщений об ошибках. Функция возвращает 1 в случае успешной проверки и 0, если обнаружены расхождения со схемой или синтаксические ошибки.
Функция ВалидацияХМЛпоСхеме(ИмяФайла, ИмяСхемы, КолвоВыводимыхОшибок = 1)
Попытка
Схема = СоздатьОбъект("MSXML2.XMLSchemaCache.6.0");
Исключение
Сообщить("Не удалось создать объект XMLSchemaCache", "!");
Возврат 0;
КонецПопытки;
Попытка
Схема.add("", ИмяСхемы);
Исключение
Сообщить("Не удалось подключить схему: " + ИмяСхемы, "!");
Возврат 0;
КонецПопытки;
Попытка
ДОМ = СоздатьОбъект("MSXML2.DOMDocument.6.0");
Исключение
Сообщить("Не удалось создать объект DOMDocument", "!");
Возврат 0;
КонецПопытки;
ДОМ.schemas = Схема;
ДОМ.async = 0;
ДОМ.validateOnParse = -1;
ДОМ.resolveExternals = -1;
Если (КолвоВыводимыхОшибок > 1) Тогда
ДОМ.SetProperty("MultipleErrorMessages", -1);
КонецЕсли;
ДОМ.load(ИмяФайла);
Если (ДОМ.parseError.errorCode <> 0) Тогда
ВывестиСообщение("При проверке по схеме файла " + ИмяФайла + " выявлены ошибки!", 0);
Если (КолвоВыводимыхОшибок > 1) Тогда
Для НомОш = 0 По ДОМ.parseError.allErrors.length - 1 Цикл
parseError = ДОМ.parseError.allErrors.item(НомОш);
ВывестиОшибкуРазбораХМЛ(parseError);
Если НомОш >= КолвоВыводимыхОшибок Тогда
ВывестиСообщение("...и еще " + (ДОМ.parseError.allErrors.length - 1 - НомОш) + " ошибок", 0);
Прервать;
КонецЕсли;
КонецЦикла;
Иначе
ВывестиОшибкуРазбораХМЛ(ДОМ.parseError);
КонецЕсли;
Возврат 0;
КонецЕсли;
ДОМ = "";
Возврат 1;
КонецФункции
Для того чтобы разработчик или пользователь мог оперативно исправить структуру выгрузки, необходимо получить полную информацию о месте возникновения проблемы. Объект parseError предоставляет номер строки, позицию символа, смещение и текстовое описание ошибки.
Процедура ВывестиОшибкуРазбораХМЛ(Ошибка)
ВывестиСообщение("", 0);
ВывестиСообщение("Обнаружена ошибка:", 0);
ВывестиСообщение("Смещение = " + Ошибка.filepos + ", строка = " + Ошибка.line + ", позиция = " + Ошибка.linepos + ", код ошибки = " + Ошибка.errorCode, 0);
ВывестиСообщение("Описание ошибки: " + Ошибка.reason, 0);
КонецПроцедуры
Функция ФорматироватьТекстСообщения(ТекстСообщения)
Возврат "" + ТекущаяДата() + " " + ТекущееВремя() + ": " + ТекстСообщения + РазделительСтрок;
КонецФункции
Процедура ВывестиСообщение(Сообщ, Куда = 1, Форматировать = 0)
Если (Форматировать = 1) Тогда
ТекСообщ = ФорматироватьТекстСообщения(Сообщ);
Иначе
ТекСообщ = Сообщ;
КонецЕсли;
Если (Куда = 0) ИЛИ (Куда = 2) Тогда
Сообщить(ТекСообщ, "!");
КонецЕсли;
Если (Куда = 1) ИЛИ (Куда = 2) Тогда
Протокол = Протокол + ТекСообщ;
КонецЕсли;
КонецПроцедуры
Проанализируем практический сценарий интеграции проверки в процедуру формирования электронного документа:
ПолноеИмяФайла = локПапкаОбмена + ИмяФайлаОбмена + ".xml";
ТекстВыгрузкиДанных.Записать(ПолноеИмяФайла);
Сообщить("" + ВыбДокумент + ", " + ВыбДокумент.Контрагент, "i");
Сообщить("успешно выгружен в " + ПолноеИмяФайла, "i");
ИмяСхемы = КаталогИБ() + "XSD.DIR\ON_SFAKT_1_897_01_05_01_01.xsd";
Если ФС.СуществуетФайл(ИмяСхемы) = 0 Тогда
Сообщить("Внимание: отсутствует формальная схема эл.счета-фактуры, проверка файла не выполняется...", "!");
Возврат;
КонецЕсли;
Результат = ВалидацияХМЛпоСхеме(ПолноеИмяФайла, ИмяСхемы, 10);
Если Результат <> 0 Тогда
Сообщить("Информация: сформированный файл обмена успешно прошел проверку на соответствие формальной схеме...", "i");
Иначе
Сообщить("Ошибка: сформированный файл обмена НЕ ПРОШЕЛ проверку на соответствие формальной схеме, есть ошибки...", "!");
КонецЕсли;
Разберем важные нюансы, с которыми можно столкнуться при использовании данного механизма:
Схема.add(TargetNamespace, ИмяСхемы) принимает первым параметром URI пространства имен. Если в схеме XSD объявлен атрибут targetNamespace, его значение должно в точности совпадать с переданным параметром. Если пространство имен отсутствует, передается пустая строка "".xs:import или xs:include, все вспомогательные схемы должны находиться в том же каталоге или по указанным относительным путям, а свойство ДОМ.resolveExternals обязательно должно быть установлено в -1 (True).Текст, он сохраняется в кодировке Windows-1251 (ANSI). Убедимся, что заголовок XML содержит encoding="windows-1251". Несоответствие фактической кодировки файла декларации в заголовке приведет к ошибке разбора еще до этапа проверки по XSD.xmllint) или потоковая проверка через собственную .NET-компоненту.