Удаление неиспользуемых элементов из XML-схемы с помощью XSLT

#xslt #recursion #schema

#xslt #рекурсия #схема

Вопрос:

Я ищу способ (если это вообще возможно) использовать XSL-преобразование XSD-документа для удаления неиспользуемых элементов. Это часто встречается в моей работе, когда компания определяет XSD с абсолютно всем в нем, но затем они захотят создать урезанную версию для одного корневого элемента в нем.

Для дальнейшего объяснения у меня может быть XSD, подобный следующему:

 <?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
    <xs:element name="RootElement">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="ChildElement"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:element name="ChildElement"/>
    <xs:element name="UnusedElement"/>
</xs:schema>
  

Что я хотел бы иметь возможность сделать, так это настроить XSL, где я предоставляю начальный элемент (в данном случае RootElement ), и он скопирует все зависимые элементы, но опустит неиспользуемые. В приведенном выше примере, если бы я передал rootElement, я ожидал бы увидеть, что RootElement и ChildElement включены, но UnusedElement опущены.

(Когда я говорю «предоставить начальный элемент», я с удовольствием открываю таблицу стилей и набираю xsl:template match="RootElement" там, где требуется.)

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

(Конечно, было бы еще лучше, если бы он мог делать то же самое в любых импортированных схемах!)

Я тщательно искал в Google и ничего не могу найти по этому поводу — я не уверен, означает ли это, что это невозможно или нет.

Спасибо!

Редактировать: На самом деле мне, вероятно, следует уточнить и сказать, что я хотел бы удалить неиспользуемые элементы И типы, чтобы они следовали по обеим ref="childElement" и type="someType" ссылкам.

Комментарии:

1. Хороший вопрос, 1. Смотрите мой ответ для полного решения.

2. Спасибо, Димитр. Ваше решение было близко (см. Мои комментарии ниже), но с ним все еще есть пара проблем (удаляет определения типов, не охватывает импортированные схемы). Возможно, мой образец XML был чрезмерно упрощен. Возможно, это нереально решаемая проблема с декларативным языком или, по крайней мере, не стоит усилий в попытке указать XSLT, как это сделать. Однако, если в ближайшие день-два не всплывет что-то неожиданное, я отмечу ваш ответ как (ближайший). Спасибо!

Ответ №1:

Это преобразование:

 <xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema" >
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:param name="ptopElementName" select="'RootElement'"/>

 <xsl:variable name="vTop" select=
 "/*/xs:element[@name=$ptopElementName]"/>

 <xsl:variable name="vNames"
      select="$vTop/descendant-or-self::*/@name"/>

 <xsl:variable name="vRefs"
      select="$vTop/descendant-or-self::*/@ref"/>

 <xsl:variable name="vTypes"
      select="$vTop/descendant-or-self::*/@type"/>

 <xsl:template match="node()|@*" name="identity">
     <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
 </xsl:template>

 <xsl:template match="xs:element">
  <xsl:if test=
    "@name=$vNames
    or
     @name=$vRefs
    or
     ancestor-or-self::*[@name=$ptopElementName]">
   <xsl:call-template name="identity"/>
  </xsl:if>
 </xsl:template>

 <xsl:template match="xs:complexType|xs:simpleType">
  <xsl:if test=
   "@name=$vTypes
    or
     ancestor-or-self::*[@name=$ptopElementName]">
   <xsl:call-template name="identity"/>
  </xsl:if>
 </xsl:template>
</xsl:stylesheet>
  

при применении к предоставленному XML-документу:

 <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
    <xs:element name="RootElement">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="ChildElement"/>
            </xs:sequence>
        </xs:complexType></xs:element>
    <xs:element name="ChildElement"/>
    <xs:element name="UnusedElement"/>
</xs:schema>
  

выдает желаемый, правильный результат:

 <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
   <xs:element name="RootElement">
      <xs:complexType>
         <xs:sequence>
            <xs:element ref="ChildElement"/>
         </xs:sequence>
      </xs:complexType>
   </xs:element>
   <xs:element name="ChildElement"/>
</xs:schema>
  

Комментарии:

1. Смелая попытка, но мне кажется, что это не сработает, если документ схемы имеет целевое пространство имен или если он включает / импортирует другие документы схемы. Обрабатывать XSD-документы в их полном объеме с использованием XSLT сложно — это вполне возможно, если вы знаете, что используете только подмножество языка XSD, но очень сложно, если использование XSD не ограничено. (Также обратите внимание, что объявление элемента может казаться «неиспользуемым», но все равно влияет на то, что разрешено в строгом подстановочном знаке ( <xs:any processContents="strict"/> )

2. @Michael-Kay: Да, я полностью осознаю, что это решение очень ограничено заявленными требованиями OP. Мне нужно было бы хорошо обновить мои устаревшие знания XSD, чтобы утверждать, что решение охватывает даже все основные случаи. Кроме того, мне кажется, что ссылки на имена и типы являются транзитивными и, как таковые, должны выполняться во много этапов, чтобы построить транзитивное замыкание отношения «ссылки / referenced» — это, конечно, здесь не делается. Было бы много работы даже за вознаграждение.

3. Спасибо, Димитр, это, безусловно, довольно классное решение (и быстрый ответ). К сожалению, Майкл был прав насчет целевого пространства имен — мой образец был очень упрощенной версией, тогда как в моих реальных схемах есть целевые пространства имен. Интересно, что он также удалил XML-объявление и все определения типов (не уверен, что удаление типов связано с одним и тем же).

4. Майкл, к счастью, мы используем только довольно простое подмножество спецификации XSD, в основном просто элементы, ссылки на элементы и сложные типы / простые типы, расширяющие другие типы. Я не верю, что существуют какие-либо расширяемые части схемы с использованием xs: any или чего-либо еще. Я не уверен, что какое-либо простое решение смогло бы помочь с этим …! Другая наша большая проблема заключается в том, что схемы разбиты на несколько файлов с использованием импорта. Их «Очистка» — гораздо более серьезная проблема! Возможно, мне придется прибегнуть к коду, например, Java …?

5. @Chris: одна вещь, которую вы могли бы рассмотреть, — начать не с необработанных XSD-документов, а с документов SCM, созданных с использованием опции -scmout процессора Saxon schema. По сути, это дает вам XML-представление «компонентов схемы» в канонической форме, где вам не нужно беспокоиться обо всех деталях xs: import / xs: include, группах, локальных и глобальных объявлениях, префиксах пространства имен и всех других вариантах документов схемы XSD.