XSL для создания всех имен элементов и атрибутов - PullRequest
0 голосов
/ 15 ноября 2018

Мне нужны две таблицы стилей XSL, которые оценивают любой XML-документ и возвращают:

1) все имена элементов в их иерархии;

2) все имена атрибутов, связанные с каждым элементом; и

3) одна таблица стилей, которая выводит результат в XML; одна таблица стилей, которая выводит результат в текст.

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

Обратите внимание, что я обладаю минимальными знаниями по XSL / XML. Я могу использовать неправильные термины. Также я могу пропустить что-то очевидное в моей просьбе. Поэтому я бы рассчитывал, что вы примените ваши идеи.

Пример XML:

<BOOK id="1" chapters="9">
    <AUTHOR gender="Male" age="43">
        <NAME>John Smith</NAME>
    </AUTHOR>
    <TITLE>Just a book</TITLE>
</BOOK>

Требуемый текстовый вывод (два столбца, разделенные символом табуляции или каким-либо символом):

element    attribute
/BOOK    id
/BOOK    chapters
/BOOK/AUTHOR    gender
/BOOK/AUTHOR    age
/BOOK/AUTHOR/NAME 
/BOOK/TITLE

Требуемый вывод XML (более или менее? Не уверен; открыт для предложений):

<ROOT>
    <ELEMENT>/BOOK</ELEMENT><ATTRIBUTE>id</ATTRIBUTE>
    <ELEMENT>/BOOK</ELEMENT><ATTRIBUTE>chapters</ATTRIBUTE>
    <ELEMENT>/BOOK/AUTHOR</ELEMENT>gender<ATTRIBUTE></ATTRIBUTE>
    <ELEMENT>/BOOK/AUTHOR</ELEMENT>age<ATTRIBUTE></ATTRIBUTE>
    <ELEMENT>/BOOK/AUTHOR/NAME</ELEMENT>
    <ELEMENT>/BOOK/TITLE</ELEMENT>
</ROOT>

Я не хочу быть пустяком, и это больше, чем маленький вопрос, поэтому я готов заплатить за ответ.

Спасибо.

Ответы [ 2 ]

0 голосов
/ 15 ноября 2018

Во-первых, требования:

(a) в вашем примере нет ни одного случая, когда существует более одного элемента или атрибута с одним и тем же путем, поэтому неясно, хотите ли вы удалить дубликаты.

(b) ваш выходной XML довольно сложно обработать, потому что пути к элементам и имена атрибутов не связаны, кроме как относительной позицией.Было бы лучше добавить элемент обертки, скажем, <PATH>.

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

<ROOT>
  <PATH>    
    <ELEMENT>/BOOK</ELEMENT>
    <ATTRIBUTE>id</ATTRIBUTE>
    <ATTRIBUTE>chapters</ATTRIBUTE>
  </PATH>
  <PATH>
    <ELEMENT>/BOOK/AUTHOR</ELEMENT>
    <ATTRIBUTE>gender<ATTRIBUTE>
    <ATTRIBUTE>age</ATTRIBUTE>
  <PATH>
    <ELEMENT>/BOOK/AUTHOR/NAME</ELEMENT>
  </PATH>
  <PATH>
    <ELEMENT>/BOOK/AUTHOR/TITLE</ELEMENT>
  </PATH>
</ROOT>

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

Сначала нам нужна функция, которая дает путь к элементу:

<xsl:function name="f:path" as="xs:string">
  <xsl:param name="node" as="node()"/>
  <xsl:choose>
    <xsl:when test="exists($node/..)">
      <xsl:sequence select="concat(f:path($node/..), '/', local-name($node)"/>
    </xsl:when>
    <xsl:otherwise>/</xsl:otherwise>
  </xsl:choose>
</xsl:function>

Теперь начинается группировка:

<xsl:for-each-group select="//*" group-by="f:path(.)">
  <PATH>
    <ELEMENT><xsl:value-of select="current-grouping-key()"/></ELEMENT>
    <xsl:for-each-group select="current-group()/@*" group-by="local-name()">
      <ATTRIBUTE><xsl:value-of select="local-name()"/></ATTRIBUTE>
    </xsl:for-each-group>
  </PATH>
</xsl:for-each-group>
0 голосов
/ 15 ноября 2018

Вы можете сгенерировать желаемый XML с помощью следующей таблицы стилей:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="2.0">
    <xsl:output indent="yes"/>

    <xsl:template match="/">
        <ROOT>
        <xsl:apply-templates select="//*[not(@*)]|//@*" />
        </ROOT>
    </xsl:template>

    <xsl:template match="*">
        <ELEMENT><xsl:apply-templates select="." mode="path"/></ELEMENT>
    </xsl:template>

    <xsl:template match="@*">
        <xsl:apply-templates select=".." />
        <ATTRIBUTE><xsl:value-of select="name()"/></ATTRIBUTE>
    </xsl:template>

    <xsl:template match="*" mode="path">
        <xsl:text>/</xsl:text>
        <xsl:value-of select="ancestor-or-self::*/name(.)" separator="/"/>
    </xsl:template>

</xsl:stylesheet>

, а затем улучшить ее, чтобы иметь возможность генерировать желаемый XML или текстовый вывод, задав значение формата параметр для «текста».Установка значения параметра format на что-либо еще будет генерировать вывод XML.Вы можете настроить значение разделителя, установив другое значение для параметра delim .

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="2.0">
    <xsl:output indent="yes" omit-xml-declaration="yes"/>

    <xsl:param name="format" select="'text'"/>

    <xsl:param name="delim" select="'   '"/>

    <xsl:template match="/">
      <!--generate XML report and assign to a variable-->
      <xsl:variable name="report">
        <ROOT>
          <!--Push elements that don't have attributes, and all attributes.
              Elements that have attributes will be transformed when
              transforming the attributes -->
          <xsl:apply-templates select="//*[not(@*)] | //@*" />
        </ROOT>
      </xsl:variable>
      <!--depending upon the value of the format parameter, 
          either transform that XML into the text report,
          or return the generated XML -->  
      <xsl:choose>
        <xsl:when test="$format='text'">
            <xsl:apply-templates select="$report/ROOT"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:sequence select="$report"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:template>

    <xsl:template match="*">
        <ELEMENT><xsl:apply-templates select="." mode="path"/></ELEMENT>
    </xsl:template>

    <!--for each attribute, generate an ELEMENT with the path and an ATTRIBUTE element-->
    <xsl:template match="@*">
        <xsl:apply-templates select=".." />
        <ATTRIBUTE><xsl:value-of select="name()"/></ATTRIBUTE>
    </xsl:template>

    <xsl:template match="*" mode="path">
        <xsl:text>/</xsl:text>
        <xsl:value-of select="ancestor-or-self::*/name(.)" separator="/"/>
    </xsl:template>

    <!--templates to transform the generated XML into the text output -->
    <xsl:template match="ROOT">
        <xsl:sequence select="string-join(('element', 'attribute'), $delim)"/>
        <xsl:apply-templates select="ELEMENT"/>
    </xsl:template>

    <xsl:template match="ELEMENT">
      <xsl:value-of select="'&#10;'"/>
      <xsl:value-of select="string-join((., following-sibling::*[1][self::ATTRIBUTE]), $delim)"/>
    </xsl:template>

</xsl:stylesheet>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...