xsl:sequence
代替例如xsl:value-of
最后,我需要创建一种方便的方法来读取多个配置文件,以控制复杂xsl转换(当前为2.0)的处理。每个配置文件可能具有也可能没有特定的节点。配置文件之间存在相对优先级,并且任何特定值的最终值应来自该值所在的最高优先级配置文件。
下面是一个带有一个变量的简单配置文件(so.xml):
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns="urn:config.template.config" >
<primary>Yes, this is primary</primary>
</config>
Old Method:通过在单个配置文件中为节点“ primary”将参数设置为值“ primary”来读取一个文件:
<xsl:param name="primary" select="$primaryConfig/myConfig:config/myConfig:primary/text()"/>
Now:我可能有最多四个配置文件,这些配置文件的may具有“ primary”作为值。为此,我选择编写两个模板。 pickConfigNode
将在模板文件中搜索(使用选择对读取进行优先级排序),以查看所请求的节点的值是否包含在“ level1”中。
<xsl:template name="pickConfigNode">
<xsl:param name="level1"/>
<xsl:choose>
<xsl:when test="$primaryConfig/myConfig:config/*[local-name() = $level1]">
<xsl:value-of select="$primaryConfig/myConfig:config/*[local-name() = $level1]"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$secondaryConfig/myConfig:config/*[local-name() = $level1]"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
这对我来说足够好了[[只要该值存在于某些配置文件中](与搜索主服务器和辅助服务器一起显示)。但是,可能未在任何地方定义该值。我想我想返回一个空序列,如果old method不存在该节点,会发生什么。但是,我可能对“ *”如何工作一无所知感到误解。
pickConfigNode
返回部分文档。因此,这会导致pickConfigText
出现问题:<xsl:template name="pickConfigText" as="xs:string">
<xsl:param name="level1"/>
<xsl:variable name="chosenNode">
<xsl:call-template name="pickConfigNode">
<xsl:with-param name="level1" select="$level1"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="$chosenNode/text()"/>
</xsl:template>
这里是辅助文件:
<?xml version="1.0" encoding="UTF-8"?> <config xmlns="urn:config.template.config" > <onlySecondary>from secondary</onlySecondary> <primary>No, this is secondary</primary> </config>
这里是使用两个配置文件的完整测试用例:
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:myConfig="urn:config.template.config" xpath-default-namespace="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="myConfig xs" > <xsl:output method="xml" omit-xml-declaration="no" indent="yes" encoding="us-ascii" cdata-section-elements="p i b u li"/> <xsl:variable name="configFile" select="'so.xml'"/> <xsl:variable name="primaryConfig" select="document($configFile)"/> <xsl:variable name="secondaryConfig" select="document('second.xml')"/> <xsl:template name="pickConfigNode"> <xsl:param name="level1"/> <xsl:choose> <xsl:when test="$primaryConfig/myConfig:config/*[local-name() = $level1]"> <xsl:value-of select="$primaryConfig/myConfig:config/*[local-name() = $level1]"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="$secondaryConfig/myConfig:config/*[local-name() = $level1]"/> </xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template name="pickConfigText" as="xs:string"> <xsl:param name="level1"/> <xsl:variable name="chosenNode"> <xsl:call-template name="pickConfigNode"> <xsl:with-param name="level1" select="$level1"/> </xsl:call-template> </xsl:variable> <xsl:value-of select="$chosenNode/text()"/> </xsl:template> <xsl:param name="primary"> <xsl:call-template name="pickConfigText"> <xsl:with-param name="level1" select="'primary'"/> </xsl:call-template> </xsl:param> <xsl:param name="onlySecondary"> <xsl:call-template name="pickConfigText"> <xsl:with-param name="level1" select="'onlySecondary'"/> </xsl:call-template> </xsl:param> <xsl:param name="neither"> <xsl:call-template name="pickConfigText"> <xsl:with-param name="level1" select="'neither'"/> </xsl:call-template> </xsl:param> <xsl:template match="/"> <PRIMARY> <xsl:choose> <xsl:when test="$primary"><SUCCESS><xsl:value-of select="$primary"/></SUCCESS></xsl:when> <xsl:otherwise> <FAILURE> <xsl:value-of select="'No value for primary'"/> </FAILURE> </xsl:otherwise> </xsl:choose> </PRIMARY> <SECONDARY> <xsl:choose> <xsl:when test="$onlySecondary"><SUCCESS><xsl:value-of select="$onlySecondary"/></SUCCESS></xsl:when> <xsl:otherwise> <FAILURE> <xsl:value-of select="'No value for onlySecondary'"/> </FAILURE> </xsl:otherwise> </xsl:choose> </SECONDARY> <NEITHER> <xsl:choose> <xsl:when test="not($neither)"><SUCCESS>NOT in either file</SUCCESS></xsl:when> <xsl:otherwise> <FAILURE> <xsl:value-of select="'Got value of for neither='"/> <xsl:value-of select="$neither"/> </FAILURE> </xsl:otherwise> </xsl:choose> </NEITHER> </xsl:template> </xsl:stylesheet>
输出为:
<?xml version="1.0" encoding="us-ascii"?> <PRIMARY> <SUCCESS>Yes, this is primary</SUCCESS> </PRIMARY> <SECONDARY> <SUCCESS>from secondary</SUCCESS> </SECONDARY> <NEITHER> <FAILURE>Got value of for neither=</FAILURE> </NEITHER>
所以,我希望NEITHER结果为“ SUCCESS”。感谢您在帮助我理解xslt处理方面的误解的帮助。另外,如果您有其他方法来处理优先配置文件,我也很想听听。
最终,我需要创建一种方便的方法来读取多个配置文件,用于控制复杂xsl的处理转换(当前为2.0)。每个配置文件可能或可能没有特定的节点。两者之间存在相对优先级配置文件,以及任何特定值的最终值应来自优先级最高的配置文件,其中值存在。
这是一种根据预定义的优先级从多个文件中提取值的通用技术]
让我们在C:\temp\DeleteMe
目录中拥有这四个配置文件-那里没有其他.xml文件:Config1.xml
<config xmlns="urn:config.template.config" >
<exists>Yes</exists>
</config>
Config2.xml
<config xmlns="urn:config.template.config" >
<SomethingElse>Yes</SomethingElse>
</config>
Config3.xml
<config xmlns="urn:config.template.config" >
<exists>YesConfig3</exists>
</config>
Config4.xml
<config xmlns="urn:config.template.config" >
<SomethingElseEvenMore>Yes</SomethingElseEvenMore>
</config>
请注意,只有Config1.xml和Config3.xml具有<exists>
元素。此转换
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:my="my:my"
xmlns:x="urn:config.template.config" >
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="pDirectory" as="xs:string" select="'file:///c:/temp/DeleteMe'"/>
<xsl:variable name="vConfigs" select="collection(concat($pDirectory, '?select=*.xml'))"/>
<xsl:template match="/">
<xsl:value-of select="my:GetConfigValue('exists', $vConfigs)"/>
</xsl:template>
<xsl:function name="my:GetConfigValue">
<xsl:param name="pConfigName" as="xs:string"/>
<xsl:param name="pConfigs" as="document-node()*"/>
<xsl:variable name="vConfigsMatching" as="document-node()*">
<xsl:perform-sort select="$pConfigs[*/*[name() eq $pConfigName and text()]]">
<xsl:sort
select="number(substring-before(substring-after(base-uri(.), 'Config'), '.xml'))"
order="descending"/>
</xsl:perform-sort>
</xsl:variable>
<xsl:sequence select="$vConfigsMatching[1]/*/*[name() eq $pConfigName]/text()"/>
</xsl:function>
</xsl:stylesheet>
当应用于任何xml文件(未使用)时,将配置文件名称中的数字用作优先级-因此,从高到低的优先级是:
转换正确地从最高优先级的congfig文件生成了配置项"exists"
的值
YesConfig3
:如果我们要求一个不存在的元素的值
<xsl:value-of select="my:GetConfigValue('not-found', $vConfigs)"/>
函数正确返回空序列-上面的count()
为0。如果我们删除Config3.xml中
<exists>
的文本节点子级,则该函数正确地返回Config1.xml中<exists>
的字符串值“
Yes
如果还要在Config1.xml中删除<exists>
的文本节点子代,则>该函数正确地不返回任何文本节点-空序列-通过返回的结果序列的计数来确认为零。
xsl:sequence
代替例如xsl:value-of
<xsl:sequence select="$primaryConfig/myConfig:config/*[local-name() = $level1]"/>
如果您随后在命名函数而不是命名模板中执行此操作,那么您也可以坚持使用<xsl:param name="foo" select="my:pickConfigText('arg')"/>
,并且您的代码当然只会选择现有节点或空序列。使变量包含文档片段的整个问题是由于您将
xsl:param/xsl:variable
与嵌套的xsl:call-template
一起使用,而不在要创建片段的as
上使用xsl:param/xsl:variable
属性。
我也有点担心表现。我的配置文件可能很大(并且具有多个配置级别)。如果可能的话,我不想搜索多个结构。因此,我更喜欢我的选择结构。我对节点选择器使用'*'感到不满意,但是我没有其他方法可以提供一般的解决方案。
我还添加了可能使用默认值的函数(configTextDefault1
)。
我还有很多要学习。感谢@DimitriNovatchev和@MartinHonnen。我仍然需要更全面地了解类型和处理模型。
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:myConfig="urn:config.template.config"
xmlns:myApp="urn:my.app"
xpath-default-namespace="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="myConfig xs"
>
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="primaryConfig" select="document('so.xml')"/>
<xsl:variable name="secondaryConfig" select="document('second.xml')"/>
<xsl:variable name="tertiaryConfig" select="document('noExist.xml')"/> <!-- test what happens if the file does not exist -->
<xsl:function name="myApp:pickConfigNode1" as="node()*">
<xsl:param name="level1" as="xs:string"/>
<xsl:choose>
<xsl:when test="$primaryConfig/myConfig:config/*[local-name() = $level1]">
<xsl:sequence select="$primaryConfig/myConfig:config/*[local-name() = $level1]"/>
</xsl:when>
<xsl:when test="$secondaryConfig/myConfig:config/*[local-name() = $level1]">
<xsl:sequence select="$secondaryConfig/myConfig:config/*[local-name() = $level1]"/>
</xsl:when>
<xsl:when test="$tertiaryConfig/myConfig:config/*[local-name() = $level1]">
<xsl:sequence select="$tertiaryConfig/myConfig:config/*[local-name() = $level1]"/>
</xsl:when>
</xsl:choose>
</xsl:function>
<xsl:function name="myApp:configText1">
<xsl:param name="level1" as="xs:string"/>
<xsl:variable name="chosenNode" select="myApp:pickConfigNode1($level1)"/>
<xsl:sequence select="$chosenNode/text()"/>
</xsl:function>
<xsl:function name="myApp:configTextDefault1">
<xsl:param name="level1" as="xs:string"/>
<xsl:param name="default" as="xs:string"/>
<xsl:variable name="chosenText" select="myApp:configText1($level1)"/>
<xsl:choose>
<xsl:when test="$chosenText">
<xsl:sequence select="$chosenText"/>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="($default)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<xsl:template match="/">
<PRIMARY>
<xsl:choose>
<xsl:when test="myApp:configText1('primary')"><SUCCESS><xsl:value-of select="myApp:configText1('primary')"/></SUCCESS></xsl:when>
<xsl:otherwise>
<FAILURE>
<xsl:value-of select="'No value for primary'"/>
</FAILURE>
</xsl:otherwise>
</xsl:choose>
</PRIMARY>
<SECONDARY>
<xsl:choose>
<xsl:when test="myApp:configText1('onlySecondary')"><SUCCESSS><xsl:value-of select="myApp:configText1('onlySecondary')"/></SUCCESSS></xsl:when>
<xsl:otherwise>
<FAILURE>
<xsl:value-of select="'No value for onlySecondary'"/>
</FAILURE>
</xsl:otherwise>
</xsl:choose>
</SECONDARY>
<NEITHER>
<xsl:choose>
<xsl:when test="not(myApp:configText1('neither'))"><SUCCESS>NOT in any file</SUCCESS></xsl:when>
<xsl:otherwise>
<FAILURE>
<xsl:value-of select="'Got value of for neither: '"/>
<xsl:value-of select="myApp:configText1('neither')"/>
</FAILURE>
</xsl:otherwise>
</xsl:choose>
</NEITHER>
<DEFAULT>
<xsl:choose>
<xsl:when test="myApp:configTextDefault1('neither','default value') = 'default value'">
<SUCCESS><xsl:text>Got 'default value'</xsl:text></SUCCESS></xsl:when>
<xsl:otherwise>
<FAILURE>
<xsl:value-of select="'Got wrong value: '"/>
<xsl:value-of select="myApp:configTextDefault1('neither','default value')"/>
</FAILURE>
</xsl:otherwise>
</xsl:choose>
</DEFAULT>
</xsl:template>
</xsl:stylesheet>
xsl:sequence
代替例如xsl:value-of
我也有点担心表现。我的配置文件可能很大(并且具有多个配置级别)。如果可能的话,我不想搜索多个结构。因此,我更喜欢我的选择结构。我对节点选择器使用'*'感到不满意,但是我没有其他方法可以提供一般的解决方案。