Попытка
ТекСхема=Новый СхемаXML;
ТекСхема.РасположениеСхемы=ИмяФайлаСхемы;
НаборСхем=Новый НаборСхемXML;
НаборСхем.Добавить(ТекСхема);
ХМЛ=Новый ЧтениеXML;
ХМЛ.ОткрытьФайл(ИмяФайла,Новый ПараметрыЧтенияXML(,,,ТипПроверкиXML.СхемаXML),НаборСхем);
Пока ХМЛ.Прочитать() Цикл
КонецЦикла;
ХМЛ.Закрыть();
Исключение
ТекстОшибки=ОписаниеОшибки();
Сообщить(ТекстОшибки);
КонецПопытки;
Показать
если верить СП, всё должно работать. но не работает, выдаёт ошибку:
Ошибка при вызове метода контекста (Прочитать): Ошибка разбора XML: - [2,32]
Ошибка:
Element 'Файл': No matching global declaration available for the validation root.
в инете не нашёл ни одного рабочего примера валидации файла по схеме штатными методами 1С - ни через ЧтениеXML, ни через ФабрикаXDTO (здесь файл с ошибками спокойно грузится безо всяких сообщений о несоответствии схеме)
у кого-нибудь есть пример рабочего кода для валидации штатными средствами 1С? (если что, через msxml я знаю, как, интересуют именно штатные )
платформа 8.2.15.319, если что. но, судя по обилию в инете старых веток с аналогичными проблемами, баян играет уже давно.
или тут есть какой-то нюанс, или это просто тупо не работает, а в СП враньё. (вообще, по моим личным наблюдениям, что касательно конкретно объектов в 1С, связанных с XML, то около четверти того, что прописано в СП, не работает, работает не всегда, или работает не так, как, казалось бы, должно работать исходя из описания)
Процедура ВалидацияXML(СтрокаXML)
Попытка
// Для начала получим все пространства имен, которые встречаются в строке XML,
// и заполним множество URL пространств имен.
МножествоПИ_ = Новый Соответствие;
// Для этого нам потребуются разделители, чтобы определить начала директив xmlns.
Разделители_ = " " + Символы.ПС + Символы.ВК + Символы.Таб;
// Для удобства запомним строку xmlns
Строка_xmlns_ = "xmlns";
// и ее длину.
Длина_xmlns_ = СтрДлина(Строка_xmlns_);
// Начнем искать подстроки "xmlns" в строке XML.
Поз_ = СтрНайти(СтрокаXML, Строка_xmlns_);
Пока Не 0 = Поз_ Цикл
// Вместо GOTO. Прервать означает GOTO за конец цикла.
Пока Истина Цикл
// Будем считать, что URL пространств имен заключены в двойные кавычки,
// хотя стандарт XML допускает и одинарные. Также не будем разрешать разделители
// перед и после знака "=". Надо уточнить, допустимы ли они по стандарту XML.
НачалоURL_ = СтрНайти(СтрокаXML, "=""", , Поз_ + Длина_xmlns_);
Если 0 = НачалоURL_ Тогда
Прервать;
КонецЕсли;
КонецURL_ = СтрНайти(СтрокаXML, """", , НачалоURL_ + 2);
Если 0 = КонецURL_ Тогда
Прервать;
КонецЕсли;
// Имя пространства имен - это часть строки между концом подстроки "xmlns" и началом URL.
// Будем считать, что двоеточие также относится к имени.
ИмяПИ_ = Сред(СтрокаXML, Поз_ + Длина_xmlns_, НачалоURL_ - Поз_ - Длина_xmlns_);
// Имя не должно содержать разделителей, символов "<", ">" и двойной кавычки.
Если Не 0 = СтрНайти(Разделители_ + "<"">", ИмяПИ_) Тогда
Прервать;
КонецЕсли;
// Если имя не пусто, оно должно начинаться с двоеточия.
Если ЗначениеЗаполнено(ИмяПИ_) И Не СтрНачинаетсяС(ИмяПИ_, ":") Тогда
Прервать;
КонецЕсли;
// URL пространства имен - это часть строки после xmlns:ИмяПИ=, заключенная в кавычки.
URLПИ_ = Сред(СтрокаXML, НачалоURL_ + 2, КонецURL_ - НачалоURL_ - 2);
// URL не должен содержать разделителей, символов "<", ">" и двойной кавычки.
Если Не 0 = СтрНайти(Разделители_ + "<"">",URLПИ_) Тогда
Прервать;
КонецЕсли;
// Добавим найденный url пространства имен в множество пространств имен.
МножествоПИ_.Вставить(URLПИ_, Истина);
// Не забудем перейти за конец цикла, поскольку это вовсе и не цикл,
// а завуалированный оператор GOTO.
Прервать;
КонецЦикла;
// Переходим к следующему вхождению подстроки "xmlns".
Поз_ = СтрНайти(СтрокаXML, Строка_xmlns_, , Поз_ + Длина_xmlns_);
КонецЦикла;
// Теперь на основе найденных URL пространств имен создадим набор схем XML.
НаборСхем_ = Новый НаборСхемXML;
МассивПИ_ = Новый Массив;
Для Каждого КлючИЗначение_ Из МножествоПИ_ Цикл
МассивПИ_.Добавить(КлючИЗначение_.Ключ);
// Получать схему можно разными путями, например, извлечь её из описания wsdl веб-сервиса.
// Обратим внимание, что если схема содержит тег <xs:include/>, то предложенный здесь
// алгоритм не работает, поскольку в этом случае схема для одного пространства имен
// задается в нескольких файлах.
СхемаXML_ = ПолучитьСхемуXMLИзWSDL(КлючИЗначение_.Ключ);
НаборСхем_.Добавить(СхемаXML_);
КонецЦикла;
// По какой-то непонятной причине мы не можем напрямую использовать набор схем.
// Возможно, это ошибка платформы.
// Поэтому прогоним набор схем через фабрику XDTO.
ФабрикаСервиса_ = Новый ФабрикаXDTO(НаборСхем_);
НаборСхем_ = ФабрикаСервиса_.ЭкспортСхемыXML(МассивПИ_);
// Проверим, что набор схем корректен.
НаборСхем_.Проверить();
// Теперь прочитаем XML, с учетом набора схем.
ЧтениеXML_ = Новый ЧтениеXML;
ПараметрыЧтенияXML_ = Новый ПараметрыЧтенияXML(,,,ТипПроверкиXML.СхемаXML);
ЧтениеXML_.УстановитьСтроку(СтрокаXML, ПараметрыЧтенияXML_, НаборСхем_);
// Вот здесь, собственно, выполняется валидация xml.
ЧтениеXML_.Прочитать();
ЧтениеXML_.Закрыть();
Исключение
// Если валидация по какой-то причине не прошла, то запишем это в журнал регистрации.
// Работу программы прерывать не будем.
Ошибка_ = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
ЗаписьЖурналаРегистрации(
"ВалидацияXML.Ошибка",
УровеньЖурналаРегистрации.Ошибка,
,
СтрокаXML,
Ошибка_
);
КонецПопытки;
КонецПроцедуры
Функция ПолучитьСхемуXMLИзWSDL(ПространствоИмен) Экспорт
// Предположим, что описание wsdl веб-сервиса находится в макете.
ОписаниеWSDL_ = ПолучитьОбщийМакет("МакетОписанияWSDL").ПолучитьТекст();
// Для выполнения запросов XPath нам потребуется разыменователь с пространством имен xs.
// Его будем строить на основе следующего соответствия.
СоответствиеПИ_ = Новый Соответствие;
СоответствиеПИ_.Вставить("xs", xmlns.xs());
РазыменовательПИ_ = Новый РазыменовательПространствИменDOM(СоответствиеПИ_);
// Также нам потребуется следующий тип результата XPath.
ТипРезультата_ = ТипРезультатаDOMXPath.ЛюбойНеупорядоченныйУзел;
// Для выбора схемы будем использовать такой запрос XPath.
XPath_ = СтрШаблон("//xs:schema[@targetNamespace='%1']", ПространствоИмен);
// Преобразуем описание WSDL в DOM для выполнения запросов XPath.
ЧтениеXML_ = Новый ЧтениеXML;
ЧтениеXML_.УстановитьСтроку(ОписаниеWSDL_);
ПостроительDOM_ = Новый ПостроительDOM;
ДокументDOM_ = ПостроительDOM_.Прочитать(ЧтениеXML_);
ЧтениеXML_.Закрыть();
// Выполним запрос XPath.
РезультатXPath_ =
ДокументDOM_.ВычислитьВыражениеXPath(XPath_, ДокументDOM_, РазыменовательПИ_, ТипРезультата_)
;
// Результатом запроса XPath должен быть корневой узел схемы.
КорневойУзелСхемы_ = РезультатXPath_.ОдиночныйУзелЗначение;
// В схеме могут встречаться теги <xs:import> и <xs:include>, в которых атрибут
// schemaLocation ссылается на недоступное место. Когда платформа 1С:Предприятие выполняет
// проверку на соответствие xml схеме, она пытается загрузить внешние схемы, указанные в
// этих тегах. Подождав некоторое время (несколько десятков секунд) и не получив ответ,
// платформа выдает ошибку. Чтобы этого не происходило, удалим теги <xs:import> и
// <xs:include> из схемы. Для этого также используем XPath.
ШаблонXPath_ = "./*[(local-name()='import' or local-name()='include') and namespace-uri()='%1']";
XPath_ = СтрШаблон(ШаблонXPath_, xmlns.xs());
Пока Истина Цикл
РезультатXPath_ =
ДокументDOM_.ВычислитьВыражениеXPath(XPath_, КорневойУзелСхемы_, РазыменовательПИ_, ТипРезультата_)
;
Узел_ = РезультатXPath_.ОдиночныйУзелЗначение;
Если Неопределено = Узел_ Тогда
Прервать;
КонецЕсли;
Узел_.РодительскийУзел.УдалитьДочерний(Узел_);
КонецЦикла;
// Для создания схемы используем построитель схем.
ПостроительСхем_ = Новый ПостроительСхемXML;
// Собственно, создаем схему.
СхемаXML_ = ПостроительСхем_.СоздатьСхемуXML(КорневойУзелСхемы_);
// И вернем её в вызывающий метод.
Возврат СхемаXML_;
КонецФункции
Исправленный вариант без фильтрации через фабрику XDTO.
Процедура ВалидацияXML(СтрокаXML)
Попытка
// Для начала получим все пространства имен, которые встречаются в строке XML,
// и заполним множество URL пространств имен.
МножествоПИ_ = "";
// Для этого нам потребуются разделители, чтобы определить начала директив xmlns.
Разделители_ = " " + Символы.ПС + Символы.ВК + Символы.Таб;
// Для удобства запомним строку xmlns
Строка_xmlns_ = "xmlns";
// и ее длину.
Длина_xmlns_ = СтрДлина(Строка_xmlns_);
// Начнем искать подстроки "xmlns" в строке XML.
Поз_ = СтрНайти(СтрокаXML, Строка_xmlns_);
Пока Не 0 = Поз_ Цикл
// Вместо GOTO. Прервать означает GOTO за конец цикла.
Пока Истина Цикл
// Будем считать, что URL пространств имен заключены в двойные кавычки,
// хотя стандарт XML допускает и одинарные. Также не будем разрешать разделители
// перед и после знака "=". Надо уточнить, допустимы ли они по стандарту XML.
НачалоURL_ = СтрНайти(СтрокаXML, "=""", , Поз_ + Длина_xmlns_);
Если 0 = НачалоURL_ Тогда
Прервать;
КонецЕсли;
КонецURL_ = СтрНайти(СтрокаXML, """", , НачалоURL_ + 2);
Если 0 = КонецURL_ Тогда
Прервать;
КонецЕсли;
// Имя пространства имен - это часть строки между концом подстроки "xmlns" и началом URL.
// Будем считать, что двоеточие также относится к имени.
ИмяПИ_ = Сред(СтрокаXML, Поз_ + Длина_xmlns_, НачалоURL_ - Поз_ - Длина_xmlns_);
// Имя не должно содержать разделителей, символов "<", ">" и двойной кавычки.
Если Не 0 = СтрНайти(Разделители_ + "<"">", ИмяПИ_) Тогда
Прервать;
КонецЕсли;
// Если имя не пусто, оно должно начинаться с двоеточия.
Если ЗначениеЗаполнено(ИмяПИ_) И Не СтрНачинаетсяС(ИмяПИ_, ":") Тогда
Прервать;
КонецЕсли;
// URL пространства имен - это часть строки после xmlns:ИмяПИ=, заключенная в кавычки.
URLПИ_ = Сред(СтрокаXML, НачалоURL_ + 2, КонецURL_ - НачалоURL_ - 2);
// URL не должен содержать разделителей, символов "<", ">" и двойной кавычки.
Если Не 0 = СтрНайти(Разделители_ + "<"">",URLПИ_) Тогда
Прервать;
КонецЕсли;
// Добавим найденный url пространства имен в множество пространств имен.
Если 0 = СтрНайти(МножествоПИ_, URLПИ_) Тогда
МножествоПИ_ = МножествоПИ_ + Символы.ПС + URLПИ_;
КонецЕсли;
// Не забудем перейти за конец цикла, поскольку это вовсе и не цикл,
// а завуалированный оператор GOTO.
Прервать;
КонецЦикла;
// Переходим к следующему вхождению подстроки "xmlns".
Поз_ = СтрНайти(СтрокаXML, Строка_xmlns_, , Поз_ + Длина_xmlns_);
КонецЦикла;
// Теперь на основе найденных URL пространств имен создадим набор схем XML.
НаборСхем_ = ПолучитьНаборСхемXML(МножествоПИ_);
// Проверим, что набор схем корректен.
НаборСхем_.Проверить();
// Теперь прочитаем XML, с учетом набора схем.
ЧтениеXML_ = Новый ЧтениеXML;
ПараметрыЧтенияXML_ = Новый ПараметрыЧтенияXML(,,,ТипПроверкиXML.СхемаXML);
ЧтениеXML_.УстановитьСтроку(СтрокаXML, ПараметрыЧтенияXML_, НаборСхем_);
// Вот здесь, собственно, выполняется валидация xml.
ЧтениеXML_.Прочитать();
ЧтениеXML_.Закрыть();
Исключение
// Если валидация по какой-то причине не прошла, то запишем это в журнал регистрации.
// Работу программы прерывать не будем.
Ошибка_ = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
ЗаписьЖурналаРегистрации(
"ВалидацияXML.Ошибка",
УровеньЖурналаРегистрации.Ошибка,
,
СтрокаXML,
Ошибка_
);
КонецПопытки;
КонецПроцедуры
Функция ПолучитьНаборСхемXML(ПространстваИменЧерезСимволПС)
МассивПространствИмен_ = СтрРазделить(ПространстваИменЧерезСимволПС, Символы.ПС, Ложь);
НаборСхем_ = Новый НаборСхемXML;
// Не используем цикл Для, поскольку размер массива может измениться из-за директив
// <xs:import> в схемах.
Индекс_ = 0;
Пока Индекс_ < МассивПространствИмен_.Количество() Цикл
ДобавитьВНаборСхем(НаборСхем_, МассивПространствИмен_, Индекс_);
Индекс_ = Индекс_ + 1;
КонецЦикла;
Возврат НаборСхем_;
КонецФункции
// В МассивПространствИмен могут быть добавлены пространства имен из директивы <xs:import>
Процедура ДобавитьВНаборСхем(НаборСхем, МассивПространствИмен, ИндексВМассиве) Экспорт
// Выбираем искомое пространство имен из массива.
ПространствоИмен_ = МассивПространствИмен[ИндексВМассиве];
// Текстом файла со схемой может быть содержимое xsd файла, описание wsdl или содержимое
// любого другого документа XML, содержащего тег <xs:schema>.
ТекстФайлаСоСхемой_ = ПолучитьТекстФайлаСоСхемой(ПространствоИмен_);
// Для выполнения запросов XPath нам потребуется разыменователь с пространством имен xs.
// Его будем строить на основе следующего соответствия.
СоответствиеПИ_ = Новый Соответствие;
СоответствиеПИ_.Вставить("xs", xmlns.xs());
РазыменовательПИ_ = Новый РазыменовательПространствИменDOM(СоответствиеПИ_);
// Также нам потребуется следующий тип результата XPath.
ТипРезультата_ = ТипРезультатаDOMXPath.ЛюбойНеупорядоченныйУзел;
// Для выбора схемы будем использовать такой запрос XPath.
XPath_ = СтрШаблон("//xs:schema[@targetNamespace='%1']", ПространствоИмен_);
// Преобразуем описание WSDL в DOM для выполнения запросов XPath.
ЧтениеXML_ = Новый ЧтениеXML;
ЧтениеXML_.УстановитьСтроку(ОписаниеWSDL_);
ПостроительDOM_ = Новый ПостроительDOM;
ДокументDOM_ = ПостроительDOM_.Прочитать(ЧтениеXML_);
ЧтениеXML_.Закрыть();
// Выполним запрос XPath.
РезультатXPath_ =
ДокументDOM_.ВычислитьВыражениеXPath(XPath_, ДокументDOM_, РазыменовательПИ_, ТипРезультата_)
;
// Результатом запроса XPath должен быть корневой узел схемы.
КорневойУзелСхемы_ = РезультатXPath_.ОдиночныйУзелЗначение;
// В схеме могут встречаться теги <xs:import> и <xs:include>, в которых атрибут
// schemaLocation ссылается на недоступное место. Когда платформа 1С:Предприятие выполняет
// проверку на соответствие xml схеме, она пытается загрузить внешние схемы, указанные в
// этих тегах. Подождав некоторое время (несколько десятков секунд) и не получив ответ,
// платформа выдает ошибку. Чтобы этого не происходило, удалим из тегов <xs:import> и
// <xs:include> атрибут schemaLocation. Для этого также используем XPath.
ШаблонXPath_ = "./*[(local-name()='import' or local-name()='include') and namespace-uri()='%1']";
XPath_ = СтрШаблон(ШаблонXPath_, xmlns.xs());
РезультатXPath_ =
ДокументDOM_.ВычислитьВыражениеXPath(
XPath_, КорневойУзелСхемы_, РазыменовательПИ_, ТипРезультатаDOMXPath.НеупорядоченныйИтераторУзлов
)
;
Узел_ = РезультатXPath_.ПолучитьСледующий();
Пока Не Неопределено = Узел_ Цикл
Если Узел_.ЕстьАтрибут("namespace") Тогда
// Заодно добавим импортируемое пространство имен в массив пространств имен.
ДополнительноеПИ_ = Узел_.ПолучитьАтрибут("namespace");
Если Неопределено = МассивПространствИмен.Найти(ДополнительноеПИ_) Тогда
МассивПространствИмен.Добавить(ДополнительноеПИ_);
КонецЕсли;
КонецЕсли;
Если Узел_.ЕстьАтрибут("schemaLocation") Тогда
Узел_.УдалитьАтрибут("schemaLocation");
КонецЕсли;
Узел_ = РезультатXPath_.ПолучитьСледующий();
КонецЦикла;
// Для создания схемы используем построитель схем.
ПостроительСхем_ = Новый ПостроительСхемXML;
// Собственно, создаем схему.
СхемаXML_ = ПостроительСхем_.СоздатьСхемуXML(КорневойУзелСхемы_);
// Теперь нужно обновить ДОМ схемы, иначе манипуляции с тегами <xs:import> и <xs:include>
// не будут учтены. Можно просто удалить его.
СхемаXML_.ДокументDOM = Неопределено;
// Добавляем схему в набор.
НаборСхем.Добавить(СхемаXML_);
КонецПроцедуры
Упрощённый вариант валидации файла XML по схеме XSD.
Процедура ВалидацияXML(ПутьКФайлуXML, ПутьКФайлуСхемы)
// Читаем схему и записываем её в объектную модель документа (DOM).
ЧтениеXML_ = Новый ЧтениеXML;
ЧтениеXML_.ОткрытьФайл(ПутьКФайлуСхемы);
ПостроительDOM_ = Новый ПостроительDOM;
ДокументDOM_ = ПостроительDOM_.Прочитать(ЧтениеXML_);
ЧтениеXML_.Закрыть();
// Для построения схемы получаем корневой элемент документа.
КорневойЭлементДокумента_ = ДокументDOM_.documentElement;
// Для создания схемы используем построитель схем.
ПостроительСхем_ = Новый ПостроительСхемXML;
// Cоздаем схему.
СхемаXML_ = ПостроительСхем_.СоздатьСхемуXML(КорневойЭлементДокумента_);
// Теперь нужно обновить DOM схемы, иначе действия с тегами <xs:import> и <xs:include>
// не будут учтены.
// Для этого просто удалим документ DOM.
СхемаXML_.ДокументDOM = Неопределено;
// Добавляем схему в набор.
НаборСхем_ = Новый НаборСхемXML;
НаборСхем_.Добавить(СхемаXML_);
// Проверим, что набор схем корректен.
НаборСхем_.Проверить();
// Теперь прочитаем XML, с учетом набора схем.
ЧтениеXML_ = Новый ЧтениеXML;
ПараметрыЧтенияXML_ = Новый ПараметрыЧтенияXML(,,,ТипПроверкиXML.СхемаXML);
ЧтениеXML_.ОткрытьФайл(ПутьКФайлуXML, ПараметрыЧтенияXML_, НаборСхем_);
// При вызове метода Прочитать() выполняется валидация файла XML по схеме.
ЧтениеXML_.Прочитать();
ЧтениеXML_.Закрыть();
КонецПроцедуры