При работе с внешними системами обмена данными (СБИС, Контур) или при сравнении различных версий регламентированных отчетов (НДС, Налог на прибыль, РСВ) часто возникает необходимость представить технические идентификаторы XML-файлов в понятном пользователю виде. Например, вместо технического пути Файл\Документ\НомКорр вывести "Номер корректировки".
XSD-схема (XML Schema Definition) содержит в себе не только структуру файла, но и текстовые описания каждого поля в секциях аннотаций. В рамках данной статьи мы разберем, как автоматизировать процесс извлечения этих данных, чтобы не прописывать сопоставления вручную для каждого нового формата.
Рассмотрим наиболее гибкий способ, который предложил автор темы. Поскольку XSD-схема сама по себе является XML-файлом, мы можем прочитать ее с помощью объекта ПостроительDOM и рекурсивно обойти все узлы, анализируя теги xs:element и xs:attribute.
Шаг 1. Загрузка схемы и инициализация
Для начала нам необходимо получить текст схемы (например, из макета) и построить дерево DOM. Проанализируем основной цикл инициализации:
&НаКлиенте
Процедура ИзвлечьОписанияИзСхемы(ТекстСхемы)
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(ТекстСхемы);
ПостроительДОМ = Новый ПостроительDOM;
ДокументДом = ПостроительДОМ.Прочитать(ЧтениеXML);
МассивОписаний = Новый Массив;
// Начинаем рекурсию с корневого элемента схемы
РазобратьРекурсивно(ДокументДом.ЭлементДокумента, "", МассивОписаний);
// Результат можно вывести в таблицу значений или соответствие
КонецПроцедуры
Шаг 2. Реализация рекурсивного обхода
Главная сложность заключается в правильном формировании пути к элементу и поиске вложенных тегов xs:documentation. Разберем процедуру, которая выполняет эту работу пошагово:
&НаКлиенте
Процедура РазобратьРекурсивно(УзелРодитель, Знач ПолныйПуть, МассивОписаний)
Перем Узел, УзелДокументации, Имя, Представление, ТекущийПолныйПуть, ЭтоАтрибут;
// Проверяем, является ли текущий узел элементом или атрибутом
Если ВРег(УзелРодитель.ИмяЭлемента) = ВРег("xs:element") ИЛИ ВРег(УзелРодитель.ИмяЭлемента) = ВРег("xs:attribute") Тогда
Имя = ПолучитьЗначениеАтрибута(УзелРодитель, "name");
Если ПустаяСтрока(Имя) Тогда Возврат; КонецЕсли; // Пропускаем узлы без имени (например, ссылки)
ТекущийПолныйПуть = ПолныйПуть + ?(ЗначениеЗаполнено(ПолныйПуть), "\", "") + Имя;
ЭтоАтрибут = (ВРег(УзелРодитель.ИмяЭлемента) = ВРег("xs:attribute"));
Иначе
Имя = "";
ТекущийПолныйПуть = ПолныйПуть;
ЭтоАтрибут = Ложь;
КонецЕсли;
Для Каждого Узел из УзелРодитель.ДочерниеУзлы Цикл
// Ищем блок аннотации, в котором хранится описание
Если ВРег(Узел.ИмяЭлемента) = ВРег("xs:annotation") Тогда
Если ЗначениеЗаполнено(ТекущийПолныйПуть) Тогда
Представление = "";
Для Каждого УзелДокументации из Узел.ДочерниеУзлы Цикл
Если УзелДокументации.ТипУзла = ТипУзлаDOM.Элемент И ВРег(УзелДокументации.ИмяЭлемента) = ВРег("xs:documentation") Тогда
Представление = Представление + ?(ЗначениеЗаполнено(Представление), " ", "") + УзелДокументации.ТекстовоеСодержимое;
КонецЕсли;
КонецЦикла;
Если ЗначениеЗаполнено(Представление) Тогда
МассивОписаний.Добавить(Новый Структура("Путь, Наименование", ТекущийПолныйПуть, Представление));
КонецЕсли;
КонецЕсли;
ИначеЕсли НЕ ЭтоАтрибут Тогда
// Идем глубже в дерево, если это не конечный атрибут
РазобратьРекурсивно(Узел, ТекущийПолныйПуть, МассивОписаний);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Для корректной работы этого кода нам также понадобится вспомогательная функция для получения атрибутов узла DOM:
&НаКлиенте
Функция ПолучитьЗначениеАтрибута(Узел, ИмяАтрибута)
ТекАтрибут = Узел.Атрибуты.ПолучитьИменованныйЭлемент(ИмяАтрибута);
Возврат ?(ТекАтрибут = Неопределено, "", ТекАтрибут.Значение);
КонецФункции
Проанализируем ситуацию, с которой часто сталкиваются разработчики при парсинге сложных схем ФНС. Не все элементы описаны линейно. Часто в схеме используется атрибут ref (ссылка на другой элемент) или type (указание на сложный тип, описанный отдельно).
Если в узле xs:element указан type="НаимТипа", то описание documentation может находиться не внутри самого элемента, а внутри глобального определения xs:complexType с соответствующим именем. Чтобы наше решение было универсальным, при отсутствии описания в текущем узле необходимо реализовать поиск по дереву схемы:
type.xs:complexType или xs:simpleType с таким именем.Если XSD-схема очень объемная (как, например, в декларации по НДС), рекурсивный обход всех узлов может быть медленным. В 1С мы можем использовать XPath для прямого доступа к нужным данным. Это позволит избежать лишних итераций по контейнерам типа xs:sequence или xs:choice.
Рассмотрим пример логики использования МенеджерПространствИменDOM для поиска всех описаний:
МенеджерПИ = Новый МенеджерПространствИменDOM;
МенеджерПИ.ДобавитьПространствоИмен("xs", "http://www.w3.org/2001/XMLSchema");
// Запрос выбирает все элементы, у которых есть документация
ЗапросXPath = "//xs:element[xs:annotation/xs:documentation]";
Результат = ДокументДом.ВычислитьВыражениеXPath(ЗапросXPath, ДокументДом, МенеджерПИ);
Важный нюанс: при работе со схемами всегда обращайте внимание на префиксы. В одном файле может использоваться xs:, в другом — xsd:. Рекомендуется проверять ЛокальноеИмя узла, чтобы код оставался рабочим в любых условиях.
В регламентированных отчетах многие поля принимают значения из фиксированного списка. Например, поле "Признак подписанта" может принимать значения "1" или "2". Эти расшифровки также хранятся в XSD внутри тегов xs:enumeration.
Разберем, как дополнить наш парсер для сбора этих данных. Внутри xs:simpleType мы можем найти узлы xs:restriction, а в них — коллекцию перечислений. У каждого перечисления (enumeration) есть свой тег документации. Собрав эти данные, мы сможем выводить в отчете сравнения не просто цифру "1", а текст "1 — Налогоплательщик".
Хотя автор темы предпочел прямой разбор XML, платформа 1С предоставляет мощный инструмент — ФабрикаXDTO. Мы можем создать фабрику на основании схемы:
ПостроительСхем = Новый ПостроительСхемXML;
Схема = ПостроительСхем.СоздатьСхемуXML(ДокументДом);
НаборСхем = Новый НаборСхемXML;
НаборСхем.Добавить(Схема);
МояФабрика = Новый ФабрикаXDTO(НаборСхем);
Работа через ФабрикуXDTO позволяет обращаться к свойствам типов программно, однако стоит учитывать, что платформа не всегда предоставляет прямой доступ к тексту documentation через объекты XDTO. Поэтому для задачи извлечения именно "описаний" метод прямого разбора DOM, рассмотренный выше, зачастую оказывается более эффективным и простым в реализации.
Для реализации качественного сравнения XML-файлов отчетности мы рекомендуем:
element), так и атрибуты (attribute).Такой подход позволит вашему отчету автоматически адаптироваться к любым изменениям форматов ФНС, просто подгружая актуальную XSD-схему в макет обработки.