xsl优先配置文件

问题描述 投票:1回答:3

最后,我需要创建一种方便的方法来读取多个配置文件,以控制复杂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处理方面的误解的帮助。另外,如果您有其他方法来处理优先配置文件,我也很想听听。

xml xslt sequence configuration-files
3个回答
2
投票
最终,我需要创建一种方便的方法来读取多个配置文件,用于控制复杂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文件(未使用)时,将配置文件名称中的数字用作优先级-因此,从高到低的优先级是:

    Config4.xml
  • Config3.xml
  • Config2.xml
  • Config1.xml
  • 转换正确地从最高优先级的congfig文件生成了配置项"exists"的值

  • (Config3.xml):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属性。

    最终,我最终得到的结构更接近于我更复杂的问题所需要的结构。我在目录中有许多配置文件,需要按名称选择适当的内容并提供订单。我本可以根据这些名称创建一个序列,并使用该顺序,然后更严格地遵循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>


    1
    投票
    [我认为,您宁愿使用xsl:sequence代替例如xsl:value-of

    1
    投票
    最终,我最终得到的结构更接近于我更复杂的问题所需要的结构。我在目录中有许多配置文件,需要按名称选择适当的内容并提供订单。我本可以根据这些名称创建一个序列,并使用该顺序,然后更严格地遵循Dimitre的解决方案。

    我也有点担心表现。我的配置文件可能很大(并且具有多个配置级别)。如果可能的话,我不想搜索多个结构。因此,我更喜欢我的选择结构。我对节点选择器使用'*'感到不满意,但是我没有其他方法可以提供一般的解决方案。

    © www.soinside.com 2019 - 2024. All rights reserved.