Как правильно выполнить валидацию XML-файла по XSD-схеме штатными средствами 1С

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

При работе с внешними системами обмена данными часто возникает необходимость проверить XML-файл на соответствие определенной XSD-схеме. Несмотря на то, что в Синтакс-помощнике описание объектов ЧтениеXML и НаборСхемXML кажется исчерпывающим, разработчики часто сталкиваются с ошибками вроде "No matching global declaration available for the validation root". Это происходит из-за нюансов работы платформы с пространствами имен и механизмов импорта схем.

В этой статье мы подробно разберем, почему стандартный подход может не работать, и рассмотрим рабочие программные решения для выполнения валидации штатными средствами 1С:Предприятия. Для быстрой ручной проверки файлов без написания кода можно использовать готовую обработку для валидации XML-файлов по XSD-схемам.

Почему возникает ошибка при валидации

Прежде чем перейти к коду, проанализируем основные причины неудач при использовании метода Прочитать() с типом проверки СхемаXML. Чаще всего проблема кроется в одном из следующих пунктов:

  1. Несоответствие пространств имен: Если в XSD-схеме указан targetNamespace, а в XML-файле корневой элемент не имеет соответствующего префикса или объявления xmlns, парсер не сможет сопоставить файл со схемой.
  2. Отсутствие глобального объявления: Валидатор 1С требует, чтобы корневой элемент XML был объявлен в XSD на верхнем уровне (как прямой потомок тега schema). Если элемент описан локально внутри другого типа, он не может быть корнем документа.
  3. Внешние зависимости (import/include): Платформа 1С не всегда корректно подгружает зависимые схемы по относительным путям в schemaLocation. Если основная схема ссылается на другие файлы, их нужно добавлять в НаборСхемXML вручную.

Вариант 1. Упрощенная валидация файла по схеме

Рассмотрим наиболее распространенный сценарий, когда у нас есть файл 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С является потоковой. Если ошибка находится в конце большого файла, исключение будет выброшено только при достижении соответствующего узла.

Вариант 2. Работа со схемами, содержащими внешние ссылки (import/include)

Разберем более сложную ситуацию. Часто схемы содержат теги 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С работает как единый репозиторий: когда парсер встречает незнакомый тип из другого пространства имен, он ищет его во всех схемах, добавленных в данный набор.

Вариант 3. Динамическое извлечение пространств имен из XML

Иногда мы заранее не знаем, какие пространства имен используются в 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. Он удобен тем, что позволяет не просто проверить файл, а сразу получить объектную модель данных. Однако, простое чтение через ФабрикаXDTO.ПрочитатьXML() не всегда гарантирует строгую валидацию. Для подготовки структуры типов из внешних сервисов можно применить инструмент для конвертации схем XSD в модель XDTO.

Для гарантированной проверки выполним следующие шаги:

  1. Создадим НаборСхемXML и добавим в него все необходимые схемы.
  2. Создадим ФабрикуXDTO на основании этого набора.
  3. Прочитаем XML-файл в объект XDTO.
  4. Вызовем метод ОбъектXDTO.Проверить().

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


// Пример валидации через XDTO
НаборСхем = Новый НаборСхемXML;
НаборСхем.Добавить(Схема); // Схема уже подготовлена ранее

МояФабрика = Новый ФабрикаXDTO(НаборСхем);
Чтение = Новый ЧтениеXML;
Чтение.ОткрытьФайл(ПутьКФайлу);

Попытка
    // Сначала читаем тип корневого элемента
    ДанныеXDTO = МояФабрика.ПрочитатьXML(Чтение);
    // Теперь выполняем глубокую проверку
    ДанныеXDTO.Проверить();
    Сообщить("XDTO: Проверка пройдена");
Исключение
    Сообщить("XDTO: Ошибка валидации: " + ОписаниеОшибки());
КонецПопытки;

Подведем итоги

Для успешной валидации XML в 1С необходимо соблюдать строгую дисциплину в работе с пространствами имен. Основные правила:

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

← На главную