При работе с внешними системами обмена данными часто возникает необходимость проверить XML-файл на соответствие определенной XSD-схеме. Несмотря на то, что в Синтакс-помощнике описание объектов ЧтениеXML и НаборСхемXML кажется исчерпывающим, разработчики часто сталкиваются с ошибками вроде "No matching global declaration available for the validation root". Это происходит из-за нюансов работы платформы с пространствами имен и механизмов импорта схем.
В этой статье мы подробно разберем, почему стандартный подход может не работать, и рассмотрим рабочие программные решения для выполнения валидации штатными средствами 1С:Предприятия. Для быстрой ручной проверки файлов без написания кода можно использовать готовую обработку для валидации XML-файлов по XSD-схемам.
Прежде чем перейти к коду, проанализируем основные причины неудач при использовании метода Прочитать() с типом проверки СхемаXML. Чаще всего проблема кроется в одном из следующих пунктов:
targetNamespace, а в XML-файле корневой элемент не имеет соответствующего префикса или объявления xmlns, парсер не сможет сопоставить файл со схемой.schema). Если элемент описан локально внутри другого типа, он не может быть корнем документа.schemaLocation. Если основная схема ссылается на другие файлы, их нужно добавлять в НаборСхемXML вручную.Рассмотрим наиболее распространенный сценарий, когда у нас есть файл XML и файл схемы XSD на диске. Чтобы избежать проблем с кэшированием или неправильной интерпретацией путей, воспользуемся ПостроителемDOM для предварительного разбора схемы. Это позволит нам явно контролировать содержимое схемы перед ее использованием.
Процедура ВалидацияXML(ПутьКФайлуXML, ПутьКФайлуСхемы)
// Читаем схему и записываем её в объектную модель документа (DOM)
ЧтениеСхемы = Новый ЧтениеXML;
ЧтениеСхемы.ОткрытьФайл(ПутьКФайлуСхемы);
ПостроительDOM = Новый ПостроительDOM;
ДокументDOM_Схема = ПостроительDOM.Прочитать(ЧтениеСхемы);
ЧтениеСхемы.Закрыть();
// Получаем корневой элемент документа схемы для построителя
КорневойЭлементДокумента = ДокументDOM_Схема.documentElement;
// Создаем объект схемы на основе DOM
ПостроительСхем = Новый ПостроительСхемXML;
СхемаXML = ПостроительСхем.СоздатьСхемуXML(КорневойЭлементДокумента);
// Сбрасываем привязку к DOM, чтобы изменения (если они будут)
// или специфика парсинга не мешали валидации
СхемаXML.ДокументDOM = Неопределено;
// Формируем набор схем и добавляем в него нашу схему
НаборСхем = Новый НаборСхемXML;
НаборСхем.Добавить(СхемаXML);
// Обязательно вызываем метод Проверить(), чтобы убедиться в корректности самой схемы
Попытка
НаборСхем.Проверить();
Исключение
Сообщить("Сама XSD-схема содержит ошибки: " + ОписаниеОшибки());
Возврат;
КонецПопытки;
// Настраиваем параметры чтения для выполнения валидации
ПараметрыЧтения = Новый ПараметрыЧтенияXML(,,, ТипПроверкиXML.СхемаXML);
ЧтениеXML = Новый ЧтениеXML;
Попытка
ЧтениеXML.ОткрытьФайл(ПутьКФайлуXML, ПараметрыЧтения, НаборСхем);
// Валидация происходит в момент вызова метода Прочитать()
Пока ЧтениеXML.Прочитать() Цикл
// Просто перебираем документ до конца
КонецЦикла;
ЧтениеXML.Закрыть();
Сообщить("Файл успешно прошел валидацию!");
Исключение
ТекстОшибки = ОписаниеОшибки();
Сообщить("Ошибка валидации: " + ТекстОшибки);
ЧтениеXML.Закрыть();
КонецПопытки;
КонецПроцедуры
В данном примере мы используем ПостроительСхемXML. Это "чистый" способ, который позволяет 1С гарантированно подхватить структуру схемы. Обратите внимание на цикл Пока ЧтениеXML.Прочитать() — валидация в 1С является потоковой. Если ошибка находится в конце большого файла, исключение будет выброшено только при достижении соответствующего узла.
Разберем более сложную ситуацию. Часто схемы содержат теги import или include, где атрибут schemaLocation ссылается на внешние ресурсы. Если 1С не может достучаться до этих ресурсов (например, из-за отсутствия интернета или закрытых портов), валидация зависнет или завершится ошибкой.
Проанализируем стратегию обхода этой проблемы: мы можем программно "вычистить" схему от внешних ссылок, если знаем, что все необходимые типы данных уже загружены нами вручную, или если мы хотим подавить попытки обращения к внешним узлам.
Процедура ДобавитьВНаборСхемСФильтрацией(НаборСхем, ТекстСхемы, ЦелевоеПространствоИмен)
// Используем DOM для манипуляции узлами схемы
Чтение = Новый ЧтениеXML;
Чтение.УстановитьСтроку(ТекстСхемы);
ПостроительDOM = Новый ПостроительDOM;
ДокументDOM = ПостроительDOM.Прочитать(Чтение);
Чтение.Закрыть();
КорневойУзел = ДокументDOM.documentElement;
// Создадим разыменователь для работы с XPath (нужен префикс xs для стандартов схемы)
СоответствиеПИ = Новый Соответствие;
СоответствиеПИ.Вставить("xs", "http://www.w3.org/2001/XMLSchema");
Разыменователь = Новый РазыменовательПространствИменDOM(СоответствиеПИ);
// Ищем все импорты и инклюды
ЗапросXPath = "./*[local-name()='import' или local-name()='include']";
// В реальном коде используйте правильный синтаксис XPath:
Выражение = "./*[local-name()='import' or local-name()='include']";
Результат = ДокументDOM.ВычислитьВыражениеXPath(Выражение, КорневойУзел, Разыменователь, ТипРезультатаDOMXPath.УпорядоченныйИтераторУзлов);
Узел = Результат.ПолучитьСледующий();
Пока Узел <> Неопределено Цикл
// Удаляем атрибут schemaLocation, чтобы 1С не пыталась скачивать схему
Если Узел.ЕстьАтрибут("schemaLocation") Тогда
Узел.УдалитьАтрибут("schemaLocation");
КонецЕсли;
Узел = Результат.ПолучитьСледующий();
КонецЦикла;
// Создаем схему из модифицированного DOM
ПостроительСхем = Новый ПостроительСхемXML;
НоваяСхема = ПостроительСхем.СоздатьСхемуXML(КорневойУзел);
НоваяСхема.ДокументDOM = Неопределено;
НаборСхем.Добавить(НоваяСхема);
КонецПроцедуры
Важный момент: Если вы удаляете schemaLocation, вы должны быть уверены, что схема для импортируемого пространства имен уже добавлена в НаборСхемXML отдельно. Набор схем в 1С работает как единый репозиторий: когда парсер встречает незнакомый тип из другого пространства имен, он ищет его во всех схемах, добавленных в данный набор.
Иногда мы заранее не знаем, какие пространства имен используются в XML-файле. Рассмотрим алгоритм, который анализирует строку XML, находит все объявления xmlns и пытается подобрать для них соответствующие схемы (например, из макетов или веб-сервисов).
Выясним причину, по которой это необходимо: 1С требует жесткого сопоставления. Если в файле есть xmlns:env="http://schemas.xmlsoap.org/soap/envelope/", то в наборе схем должна быть схема с targetNamespace, в точности равным этому URL.
Рассмотрим пример функции, которая собирает уникальные пространства имен из текста XML:
Функция ПолучитьМножествоПространствИмен(СтрокаXML)
Множество = Новый Соответствие;
Поз = СтрНайти(СтрокаXML, "xmlns");
Пока Поз > 0 Цикл
// Ищем начало URL после xmlns:префикс=" или xmlns="
НачалоURL = СтрНайти(СтрокаXML, "=""", , Поз + 5);
Если НачалоURL > 0 Тогда
КонецURL = СтрНайти(СтрокаXML, """", , НачалоURL + 2);
Если КонецURL > 0 Тогда
URL = Сред(СтрокаXML, НачалоURL + 2, КонецURL - НачалоURL - 2);
Множество.Вставить(URL, Истина);
КонецЕсли;
КонецЕсли;
Поз = СтрНайти(СтрокаXML, "xmlns", , Поз + 5);
КонецЦикла;
Возврат Множество;
КонецФункции
После получения списка всех URL, мы можем в цикле найти нужные XSD (например, в справочнике или общих макетах) и наполнить НаборСхемXML. Только после этого можно запускать ЧтениеXML.
Существует альтернативный способ проверки — использование ФабрикиXDTO. Он удобен тем, что позволяет не просто проверить файл, а сразу получить объектную модель данных. Однако, простое чтение через ФабрикаXDTO.ПрочитатьXML() не всегда гарантирует строгую валидацию. Для подготовки структуры типов из внешних сервисов можно применить инструмент для конвертации схем XSD в модель XDTO.
Для гарантированной проверки выполним следующие шаги:
НаборСхемXML и добавим в него все необходимые схемы.ФабрикуXDTO на основании этого набора.ОбъектXDTO.Проверить().Рассмотрим подробнее: метод Проверить() у объекта XDTO возвращает исключение, если данные не соответствуют типам, ограничениям (facets) или структуре, описанной в схеме (например, нарушена последовательность элементов или отсутствуют обязательные поля).
// Пример валидации через XDTO
НаборСхем = Новый НаборСхемXML;
НаборСхем.Добавить(Схема); // Схема уже подготовлена ранее
МояФабрика = Новый ФабрикаXDTO(НаборСхем);
Чтение = Новый ЧтениеXML;
Чтение.ОткрытьФайл(ПутьКФайлу);
Попытка
// Сначала читаем тип корневого элемента
ДанныеXDTO = МояФабрика.ПрочитатьXML(Чтение);
// Теперь выполняем глубокую проверку
ДанныеXDTO.Проверить();
Сообщить("XDTO: Проверка пройдена");
Исключение
Сообщить("XDTO: Ошибка валидации: " + ОписаниеОшибки());
КонецПопытки;
Для успешной валидации XML в 1С необходимо соблюдать строгую дисциплину в работе с пространствами имен. Основные правила:
targetNamespace в XSD и xmlns в XML.ПостроительСхемXML для корректной инициализации объектов схем.НаборСхемXML вручную.ЧтениеXML проверяет структуру "на лету" в цикле чтения, а ОбъектXDTO.Проверить() делает полную проверку уже загруженного в память объекта.Используя предложенные методы, вы сможете настроить надежный обмен данными, который будет отсекать некорректные файлы еще на этапе загрузки, минимизируя ошибки в базе данных.