XSL apply-template во внешних данных

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

XML для этого может быть

<classes>

    <class name="A" author="Mr.X" >
        <attribute name="i_" type="integer" visibility="protected" />
        <attribute name="f_" type="float" visibility="private" />
        <attribute name="c_" type="char" visibility="private" />
        <method name="foo" return="integer" visibility="public" >
            <param name="a" type="integer" />
            <param name="b" type="integer" />
        </method>
    </class> 

    <class name="B" author="Mr.Y" >
        <attribute name="s_" type="string" visibility="protected" />
        <method name="bar" visibility="public" />
    </class> 

    <class name="CA" author="Mr.Z" base="A" >
        <attribute name="d_" type="double" visibility="protected" />
    </class>

    <class name="CB" author="Mr.Z" base="B" />

    <class name="DCA" author="Mr.X" base="CA" >
        <attribute name="s_" type="string" visibility="protected" />
    </class>

</classes>

это должно быть преобразовано в

<classes>
  <class name="A" author="Mr.X">
    <attribute name="i_" type="integer" visibility="protected"/>
    <attribute name="f_" type="float" visibility="private"/>
    <attribute name="c_" type="char" visibility="private"/>
    <method name="foo" return="integer" visibility="public">
      <param name="a" type="integer"/>
      <param name="b" type="integer"/>
    </method>
  </class>
  <class name="B" author="Mr.Y">
    <attribute name="s_" type="string" visibility="protected"/>
    <method name="bar" visibility="public"/>
  </class>
  <class name="CA" author="Mr.Z">
    <attribute name="d_" type="double" visibility="protected"/>
    <!--[begin] inherited from class A by Mr.X-->
    <attribute name="i_" type="integer" visibility="protected"/>
    <attribute name="f_" type="float" visibility="private"/>
    <attribute name="c_" type="char" visibility="private"/>
    <method name="foo" return="integer" visibility="public">
      <param name="a" type="integer"/>
      <param name="b" type="integer"/>
    </method>
    <!--[end] inherited from class A-->
  </class>
  <class name="CB" author="Mr.Z">
    <!--[begin] inherited from class B by Mr.Y-->
    <attribute name="s_" type="string" visibility="protected"/>
    <method name="bar" visibility="public"/>
    <!--[end] inherited from class B-->
  </class>
  <class name="DCA" author="Mr.X">
    <attribute name="s_" type="string" visibility="protected"/>
    <!--[begin] inherited from class CA by Mr.Z-->
    <attribute name="d_" type="double" visibility="protected"/>
    <!--[begin] inherited from class A by Mr.X-->
    <attribute name="i_" type="integer" visibility="protected"/>
    <attribute name="f_" type="float" visibility="private"/>
    <attribute name="c_" type="char" visibility="private"/>
    <method name="foo" return="integer" visibility="public">
      <param name="a" type="integer"/>
      <param name="b" type="integer"/>
    </method>
    <!--[end] inherited from class A-->
    <!--[end] inherited from class CA-->
  </class>
</classes>

С помощью michael.hor257k меня есть следующий XSL, который отлично работает, если все классы были определены в одном и том же XML-файле.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="xml" encoding="ISO-8859-1" indent="yes"/>

    <xsl:strip-space elements="*"/>

    <xsl:key name="parent" match="class" use="@name" />

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

    <xsl:template match="class">
        <xsl:copy>
            <xsl:apply-templates select="@*[name()!='base']|node()"/>
            <xsl:apply-templates select="key('parent', @base)" mode="inherit"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="class" mode="inherit">
        <xsl:comment>
            <xsl:text>[begin] inherited from class </xsl:text>
            <xsl:value-of select="@name"/>
            <xsl:text> by </xsl:text>
            <xsl:value-of select="@author"/>
        </xsl:comment>
        <xsl:copy-of select="attribute | method"/>
        <xsl:apply-templates select="key('parent', @base)" mode="inherit"/>
        <xsl:comment>
            <xsl:text>[end] inherited from class </xsl:text>
            <xsl:value-of select="@name"/>
        </xsl:comment>
    </xsl:template>

</xsl:stylesheet>

или следующее преобразование, которое эквивалентно, но без ключей.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="xml" encoding="ISO-8859-1" indent="yes"/>

    <xsl:strip-space elements="*"/>

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

    <xsl:template match="class">
        <xsl:copy>
            <xsl:apply-templates select="@*[name()!='base']|node()"/>
            <xsl:apply-templates select="//class[@name=current()/@base]" mode="inherit"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="class" mode="inherit">
        <xsl:comment>
            <xsl:text>[begin] inherited from class </xsl:text>
            <xsl:value-of select="@name"/>
            <xsl:text> by </xsl:text>
            <xsl:value-of select="@author"/>
        </xsl:comment>
        <xsl:copy-of select="attribute | method"/>
        <xsl:apply-templates select="//class[@name=current()/@base]" mode="inherit"/>
        <xsl:comment>
            <xsl:text>[end] inherited from class </xsl:text>
            <xsl:value-of select="@name"/>
        </xsl:comment>
    </xsl:template>

</xsl:stylesheet>

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

Упрощенный XML, представляющий эти классы, может быть

<classes>    
    <import file="c2.xml" />

    <class name="XZ" author="Mr.B" base="Z">
        <method name="foo" visibility="public" />
    </class>
</classes>

и содержание c2.xml может быть

<classes>
    <class name="Z" author="Mr.A" >
        <attribute name="i_" type="integer" visibility="protected" />
    </class> 
</classes>

и ожидаемый результат будет

<classes>
  <class name="Z" author="Mr.A">
    <attribute name="i_" type="integer" visibility="protected"/>
  </class>
  <class name="XZ" author="Mr.B">
    <method name="foo" visibility="public"/>
    <!--[begin] inherited from class Z by Mr.A-->
    <attribute name="i_" type="integer" visibility="protected"/>
    <!--[end] inherited from class Z-->
  </class>
</classes>

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

<xsl:template match="/classes/import">
    <xsl:comment>
        <xsl:text>importing </xsl:text>
        <xsl:value-of select="@file"/>
        <xsl:text> file</xsl:text>
    </xsl:comment>
    <xsl:apply-templates select="document(@file)/classes/node()" /> 
</xsl:template>  

Таким образом, XSL будет похож на следующий при использовании ключей

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="xml" encoding="ISO-8859-1" indent="yes"/>

    <xsl:strip-space elements="*"/>

    <xsl:key name="parent" match="class" use="@name" />

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

    <xsl:template match="/classes/import">
        <xsl:comment>
            <xsl:text>importing </xsl:text>
            <xsl:value-of select="@file"/>
            <xsl:text> file</xsl:text>
        </xsl:comment>
        <xsl:apply-templates select="document(@file)/classes/node()" /> 
    </xsl:template>  

    <xsl:template match="class">
        <xsl:copy>
            <xsl:apply-templates select="@*[name()!='base']|node()"/>
            <xsl:apply-templates select="key('parent', @base)" mode="inherit"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="class" mode="inherit">
        <xsl:comment>
            <xsl:text>[begin] inherited from class </xsl:text>
            <xsl:value-of select="@name"/>
            <xsl:text> by </xsl:text>
            <xsl:value-of select="@author"/>
        </xsl:comment>
        <xsl:copy-of select="attribute | method"/>
        <xsl:apply-templates select="key('parent', @base)" mode="inherit"/>
        <xsl:comment>
            <xsl:text>[end] inherited from class </xsl:text>
            <xsl:value-of select="@name"/>
        </xsl:comment>
    </xsl:template>

</xsl:stylesheet>

или как следующий, где не используются ключи

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="xml" encoding="ISO-8859-1" indent="yes"/>

    <xsl:strip-space elements="*"/>

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

    <xsl:template match="/classes/import">
        <xsl:comment>
            <xsl:text>importing </xsl:text>
            <xsl:value-of select="@file"/>
            <xsl:text> file</xsl:text>
        </xsl:comment>
        <xsl:apply-templates select="document(@file)/classes/node()" /> 
    </xsl:template>  

    <xsl:template match="class">
        <xsl:copy>
            <xsl:apply-templates select="@*[name()!='base']|node()"/>
            <xsl:apply-templates select="//class[@name=current()/@base]" mode="inherit"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="class" mode="inherit">
        <xsl:comment>
            <xsl:text>[begin] inherited from class </xsl:text>
            <xsl:value-of select="@name"/>
            <xsl:text> by </xsl:text>
            <xsl:value-of select="@author"/>
        </xsl:comment>
        <xsl:copy-of select="attribute | method"/>
        <xsl:apply-templates select="//class[@name=current()/@base]" mode="inherit"/>
        <xsl:comment>
            <xsl:text>[end] inherited from class </xsl:text>
            <xsl:value-of select="@name"/>
        </xsl:comment>
    </xsl:template>

</xsl:stylesheet>

Преобразование, связанное с элементами импорта, прекрасно работает для классов, определенных во внешнем файле, но поведение наследования базового класса не применяется в этих классах.

(Неправильный) вывод для вышеупомянутого XSL и предыдущего XML (тот, который включает файлы c2.xml)

<classes>
  <!--importing c2.xml file-->
  <class name="Z" author="Mr.A">
    <attribute name="i_" type="integer" visibility="protected"/>
  </class>
  <class name="XZ" author="Mr.B">
    <method name="foo" visibility="public"/>
  </class>
</classes>

Обратите внимание, что класс XZ не включает методы и атрибуты из своего базового класса Z

Помните, что внешние XML-файлы могут включать в себя также элементы импорта

Я пробовал два разных подхода. Первым было использование ключей для классов, в том числе тех, которые были объявлены во внешних XML-файлах. Я потерпел неудачу с этим, потому что я не знаю внешних имен файлов заранее, чтобы сгенерировать ключи для классов, определенных в этих внешних файлах XML. Второй - применить предикаты режима "наследовать", но снова я потерпел неудачу, потому что не знаю внешних имен файлов, чтобы применить класс шаблона с режимом наследования для всех этих файлов.

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

Заранее спасибо.

1 ответ

Решение

Ключи работают только в пределах одного документа. Я бы предложил два подхода:

(а) сначала объедините все документы в один, а затем используйте свое текущее решение.

(б) вместо использования ключей создайте индекс кросс-документа в форме карты XSLT 3.0. Что-то вроде этого:

<xsl:mode name="index" on-no-match="shallow-skip"/>

<xsl:variable name="globalIndex" as="map(xs:string, element(*))">
  <xsl:map>
    <xsl:apply-templates mode="index"/>
  </xsl:map>
</xsl:variable>

<xsl:template match="class" mode="index">
  <xsl:map-entry key="@name" select="."/>
  <xsl:apply-templates mode="index"/>
</xsl:template>

<xsl:template match="import" mode="index">
  <xsl:apply-templates select="doc(@file)" mode="index"/>
</xsl:template>

а затем, где вы ранее использовали key('parent', @base)Теперь вы можете использовать $globalIndex(@base),

(c) Это решение не даст вам скорости ключей или карт, если у вашего процессора нет интеллектуального оптимизатора (такого как Saxon-EE), который индексирует вещи автоматически; но он использует только XSLT 2.0:

<xsl:variable name="allClasses" as="element(class)*">
    <xsl:apply-templates mode="index"/>
</xsl:variable>

<xsl:template match="class" mode="index">
  <xsl:sequence select="."/>
  <xsl:apply-templates mode="index"/>
</xsl:template>

<xsl:template match="import" mode="index">
  <xsl:apply-templates select="doc(@file)" mode="index"/>
</xsl:template>

<xsl:template match="node()" mode="index">
  <xsl:apply-templates mode="index"/>
</xsl:template>

а затем, где вы ранее использовали key('parent', @base)Теперь вы можете использовать $allClasses[@name=current()/@base],

Другие вопросы по тегам