Xml XSL在外部数据中应用模板

Xml XSL在外部数据中应用模板,xml,xslt,external,apply-templates,Xml,Xslt,External,Apply Templates,我想转换一个表示类及其基类的XML,其中的类包括它自己的方法和属性,以及它所有基类的方法和属性,就像OO继承一样 这方面的XML可以是 <classes> <class name="A" author="Mr.X" > <attribute name="i_" type="integer" visibility="protected" /> <attribute name="f_" type="float" vi

我想转换一个表示类及其基类的XML,其中的类包括它自己的方法和属性,以及它所有基类的方法和属性,就像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="Z" author="Mr.A" >
        <attribute name="i_" type="integer" visibility="protected" />
    </class> 
</classes>

,我有以下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>

[开始]从类继承
通过
[end]从类继承
或以下等效但没有键的转换

<?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>

[开始]从类继承
通过
[end]从类继承
现在,我想处理的是,这些类可以在主xml或其他xml文件中定义,通过导入元素与其他文件链接,这意味着外部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="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="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非常相似,但添加了以下模式以处理新元素

<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>

进口
文件
[开始]从类继承
通过
[end]从类继承
或者像下面的一样,不使用钥匙

<?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>

进口
文件
[开始]从类继承
通过
[end]从类继承
对于外部文件中定义的类,与导入元素关联的转换可以正常工作,但基类继承行为不适用于这些类

上述XSL和以前的XML(包含c2.XML文件的XML)的(错误)输出为


请注意,XZ类不包括基类Z中的方法和属性

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

<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>  
我尝试了两种不同的方法。第一种方法是为类使用键,包括那些在外部XML文件中声明的类。我失败了,因为我事先不知道外部文件名,以便为这些外部XML文件中定义的类生成密钥。第二个是应用“inherit”模式谓词,但我还是失败了,因为我不知道外部文件名,无法为所有这些文件应用带有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>

然后在您以前使用的
键('parent',@base)
,现在您可以使用
$allClasses[@name=current()/@base]

非常感谢您的及时回复。如果不使用钥匙,可能实现这一点吗?性能不是一个问题,因为XML不是那么大,所以使用非键方法可能是可行的。如果没有,我将采用你建议的第一种方法。再次感谢您抽出时间。当做Hern您可以使用类似的方法创建一个全局变量,其中包含(引用)所有文档中的所有类元素,然后使用筛选器表达式查找包含所需名称的类。请您详细说明一下上述选项好吗?。据我所知,变量是在应用模板模式之前加载的,它们的值不能修改或更新。因此,我应该提前知道外部XML文件,但事实并非如此,因为要查找的文件是XML本身的信息。我应该使用什么最低XSL版本来实现这一点?我更喜欢使用1.0版,但这不是一个要求。我扩展了选项(c)。如果您想要XSLT1.0解决方案,您可能必须使用选项(a),但我不会为您扩展它——我上一次使用1.0是在大约15年前的愤怒中,我已经忘记了克服其局限性的所有技巧。