外部数据中的 XSL 应用模板

XSL apply-template within external data

我想转换一个 XML 表示 classes 及其基础 classes,classes 包含它自己的方法和属性,加上其所有基 classes 的方法和属性,因为 OO 继承有效。

一个 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 的帮助下,如果所有 classes 都在同一个 XML 文件中定义,我有以下 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="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>

现在,我想处理这些 classes 可以在主 xml 或其他 XML 文件中定义,并与其他文件链接一个导入元素,这意味着外部 XML 可以像写在主 XML.

中一样使用

代表这些 class 的简化 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>

与导入元素关联的转换适用于外部文件中定义的 classes,但基础 class 继承行为不适用于那些 classes。

上述 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 class 不包含其基础 class Z

中的方法和属性

请注意,外部 xml 文件也可能包含导入元素

我尝试了两种不同的方法。第一个是为 classes 使用密钥,包括那些在外部 XML 文件中声明的密钥。我失败了,因为我事先不知道外部文件名,无法为这些外部 XML 文件中定义的 classes 生成密钥。第二个是应用 "inherit" 模式谓词,但我又失败了,因为我不知道外部文件名,无法为所有文件应用具有继承模式的模板 class。

任何有关如何从外部数据为 classes 应用 "inherit" 模板的帮助,将不胜感激。任何方法,有或没有钥匙,对我来说都很好。

提前致谢。

键只能在单个文档中使用。我建议两种方法:

(a) 首先将所有文档合并为一个,然后使用您当前的解决方案。

(b) 不使用键,而是以 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]