Как программно прочитать описания элементов из XSD-схемы в 1С?

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

При работе с внешними системами обмена данными (СБИС, Контур) или при сравнении различных версий регламентированных отчетов (НДС, Налог на прибыль, РСВ) часто возникает необходимость представить технические идентификаторы XML-файлов в понятном пользователю виде. Например, вместо технического пути Файл\Документ\НомКорр вывести "Номер корректировки".

XSD-схема (XML Schema Definition) содержит в себе не только структуру файла, но и текстовые описания каждого поля в секциях аннотаций. В рамках данной статьи мы разберем, как автоматизировать процесс извлечения этих данных, чтобы не прописывать сопоставления вручную для каждого нового формата.

Метод 1: Рекурсивный разбор XSD-схемы через объектную модель DOM

Рассмотрим наиболее гибкий способ, который предложил автор темы. Поскольку 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:


&НаКлиенте
Функция ПолучитьЗначениеАтрибута(Узел, ИмяАтрибута)
    ТекАтрибут = Узел.Атрибуты.ПолучитьИменованныйЭлемент(ИмяАтрибута);
    Возврат ?(ТекАтрибут = Неопределено, "", ТекАтрибут.Значение);
КонецФункции

Метод 2: Проблемы "ссылочных" элементов (ref) и сложных типов (type)

Проанализируем ситуацию, с которой часто сталкиваются разработчики при парсинге сложных схем ФНС. Не все элементы описаны линейно. Часто в схеме используется атрибут ref (ссылка на другой элемент) или type (указание на сложный тип, описанный отдельно).

Если в узле xs:element указан type="НаимТипа", то описание documentation может находиться не внутри самого элемента, а внутри глобального определения xs:complexType с соответствующим именем. Чтобы наше решение было универсальным, при отсутствии описания в текущем узле необходимо реализовать поиск по дереву схемы:

  1. Проверить наличие атрибута type.
  2. Найти в документе узел xs:complexType или xs:simpleType с таким именем.
  3. Извлечь описание из найденного типа.

Метод 3: Использование XPath для ускорения работы

Если 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:. Рекомендуется проверять ЛокальноеИмя узла, чтобы код оставался рабочим в любых условиях.

Дополнительные возможности: получение расшифровок (Enum)

В регламентированных отчетах многие поля принимают значения из фиксированного списка. Например, поле "Признак подписанта" может принимать значения "1" или "2". Эти расшифровки также хранятся в XSD внутри тегов xs:enumeration.

Разберем, как дополнить наш парсер для сбора этих данных. Внутри xs:simpleType мы можем найти узлы xs:restriction, а в них — коллекцию перечислений. У каждого перечисления (enumeration) есть свой тег документации. Собрав эти данные, мы сможем выводить в отчете сравнения не просто цифру "1", а текст "1 — Налогоплательщик".

Использование Фабрики XDTO

Хотя автор темы предпочел прямой разбор XML, платформа 1С предоставляет мощный инструмент — ФабрикаXDTO. Мы можем создать фабрику на основании схемы:


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

Работа через ФабрикуXDTO позволяет обращаться к свойствам типов программно, однако стоит учитывать, что платформа не всегда предоставляет прямой доступ к тексту documentation через объекты XDTO. Поэтому для задачи извлечения именно "описаний" метод прямого разбора DOM, рассмотренный выше, зачастую оказывается более эффективным и простым в реализации.

Резюме

Для реализации качественного сравнения XML-файлов отчетности мы рекомендуем:

Такой подход позволит вашему отчету автоматически адаптироваться к любым изменениям форматов ФНС, просто подгружая актуальную XSD-схему в макет обработки.

← На главную