xsl优先配置文件
xsl prioritized configuration files
最终,我需要创建一种方便的方法来读取多个配置文件,以控制复杂 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>
旧方法: 我通过在节点 "primary" 的单个配置文件中将参数设置为值 "primary" 来读取一个文件:
<xsl:param name="primary" select="$primaryConfig/myConfig:config/myConfig:primary/text()"/>
现在: 我可能有最多四个配置文件,可能 有 "primary" 作为值。为此,我选择编写两个模板。 pickConfigNode
是在模板文件中搜索(使用select进行读取的优先级排序)以查看请求的节点是否具有'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 方法,就会发生这种情况。但是,当没有找到任何内容时,我可能误解了“*”是如何工作的。
pickConfigNode
returns 部分文档。因此,这会导致 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>
所以,我不希望结果是 "SUCCESS"。
感谢您帮助我理解我对 xslt 处理的误解。此外,如果您有其他方法来处理优先配置文件,我也很想听听。
我认为,您宁愿使用 xsl:sequence
而不是 xsl:value-of
例如
<xsl:sequence select="$primaryConfig/myConfig:config/*[local-name() = $level1]"/>
如果您随后在命名函数而不是命名模板中执行此操作,您也可以坚持 <xsl:param name="foo" select="my:pickConfigText('arg')"/>
并且您的代码肯定只是 select 现有节点或空序列。
变量包含文档片段的整个问题是由于您将 xsl:param/xsl:variable
与嵌套 xsl:call-template
一起使用,而没有在 [=14] 上使用 as
属性=] 正在创建片段。
Ultimately, I need to create a convenient way to read multiple
configuration files for controlling the processing of a complex xsl
transformation (2.0, currently). Each configuration file may or may
not have particular nodes. A relative priority exists between the
configuration files, and the ultimate value for any particular value
should come from the highest priority configuration file in which the
value exists.
这是根据预定义的优先级从多个文件中提取值的通用技术:
让我们将这四个配置文件放在 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 文件(未使用)时,使用配置文件名称中的数字作为优先级 -- 因此从最高到最低的优先级是:
- Config4.xml
- Config3.xml
- Config2.xml
- Config1.xml
转换正确地从最高优先级的配置文件 (Config3.xml):
中生成了配置项 "exists"
的值
YesConfig3
如果我们要求一个不存在的元素的值:
<xsl:value-of select="my:GetConfigValue('not-found', $vConfigs)"/>
函数正确returns空序列--上面的count()
为0.
如果我们在 Config3.xml 中删除 <exists>
的文本节点子节点,那么函数 returns 的字符串值是正确的<exists>
在 Config1.xml"
Yes
如果我们也删除 Config1.xml 中 <exists>
的文本节点子节点,那么函数 returns 没有文本节点 - - 空序列——由返回为零的结果序列的计数确认。
最终,我得到了一个更接近我的更复杂问题所需的结构。我在一个目录中有很多配置文件,需要按名称选择适当的文件并为它们提供命令。我本可以根据这些名称创建一个序列并使用该顺序并更严格地遵循 Dimitre 的解决方案。
我也有点担心性能。我的配置文件可能相当大(并且配置有多个级别)。如果可能的话,我不想搜索多个结构;因此,我更喜欢我选择的结构。我不满意我的节点选择器有一个“*”,但我看不出有其他方法可以得到通用的解决方案。
我还添加了一个可以使用默认值的函数 (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 转换的处理(目前为 2.0)。每个配置文件可能有也可能没有特定节点。配置文件之间存在相对优先级,任何特定值的最终值应该来自该值所在的最高优先级配置文件。
下面给出了一个简单的配置文件(so.xml)和一个变量:
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns="urn:config.template.config" >
<primary>Yes, this is primary</primary>
</config>
旧方法: 我通过在节点 "primary" 的单个配置文件中将参数设置为值 "primary" 来读取一个文件:
<xsl:param name="primary" select="$primaryConfig/myConfig:config/myConfig:primary/text()"/>
现在: 我可能有最多四个配置文件,可能 有 "primary" 作为值。为此,我选择编写两个模板。 pickConfigNode
是在模板文件中搜索(使用select进行读取的优先级排序)以查看请求的节点是否具有'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 方法,就会发生这种情况。但是,当没有找到任何内容时,我可能误解了“*”是如何工作的。
pickConfigNode
returns 部分文档。因此,这会导致 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>
所以,我不希望结果是 "SUCCESS"。
感谢您帮助我理解我对 xslt 处理的误解。此外,如果您有其他方法来处理优先配置文件,我也很想听听。
我认为,您宁愿使用 xsl:sequence
而不是 xsl:value-of
例如
<xsl:sequence select="$primaryConfig/myConfig:config/*[local-name() = $level1]"/>
如果您随后在命名函数而不是命名模板中执行此操作,您也可以坚持 <xsl:param name="foo" select="my:pickConfigText('arg')"/>
并且您的代码肯定只是 select 现有节点或空序列。
变量包含文档片段的整个问题是由于您将 xsl:param/xsl:variable
与嵌套 xsl:call-template
一起使用,而没有在 [=14] 上使用 as
属性=] 正在创建片段。
Ultimately, I need to create a convenient way to read multiple configuration files for controlling the processing of a complex xsl transformation (2.0, currently). Each configuration file may or may not have particular nodes. A relative priority exists between the configuration files, and the ultimate value for any particular value should come from the highest priority configuration file in which the value exists.
这是根据预定义的优先级从多个文件中提取值的通用技术:
让我们将这四个配置文件放在 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 文件(未使用)时,使用配置文件名称中的数字作为优先级 -- 因此从最高到最低的优先级是:
- Config4.xml
- Config3.xml
- Config2.xml
- Config1.xml
转换正确地从最高优先级的配置文件 (Config3.xml):
中生成了配置项"exists"
的值
YesConfig3
如果我们要求一个不存在的元素的值:
<xsl:value-of select="my:GetConfigValue('not-found', $vConfigs)"/>
函数正确returns空序列--上面的count()
为0.
如果我们在 Config3.xml 中删除 <exists>
的文本节点子节点,那么函数 returns 的字符串值是正确的<exists>
在 Config1.xml"
Yes
如果我们也删除 Config1.xml 中 <exists>
的文本节点子节点,那么函数 returns 没有文本节点 - - 空序列——由返回为零的结果序列的计数确认。
最终,我得到了一个更接近我的更复杂问题所需的结构。我在一个目录中有很多配置文件,需要按名称选择适当的文件并为它们提供命令。我本可以根据这些名称创建一个序列并使用该顺序并更严格地遵循 Dimitre 的解决方案。
我也有点担心性能。我的配置文件可能相当大(并且配置有多个级别)。如果可能的话,我不想搜索多个结构;因此,我更喜欢我选择的结构。我不满意我的节点选择器有一个“*”,但我看不出有其他方法可以得到通用的解决方案。
我还添加了一个可以使用默认值的函数 (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>