1

鉴于此源文件:

<?xml version="1.0" encoding="utf-8"?>
<config>
  <group name="global">
    <globals>
      <item grp="db" prop="userid" value="foo"/>
      <item grp="db" prop="passwd" value="bar"/>
      <item grp="log" prop="level" value="debug"/>
      <item grp="log" prop="filename" value="red.log"/>
    </globals>
  </group>
  <group name="dev">
    <globals>
      <item grp="db" prop="server" value="dev_sql_1"/>
    </globals>
    <locals>
      <item grp="db" prop="catalog" value="red_db_local"/>
      <item grp="db" prop="passwd" value="dev_passwd"/>
      <item grp="log" prop="level" value="info"/>
    </locals>
  </group>
  <group name="qa">
    <globals>
      <item grp="db" prop="server" value="qa_sql_1"/>
      <item grp="db" prop="catalog" value="qa_db"/>  <!-- this is wonky, but may happen -->
    </globals>
    <locals>
      <item grp="db" prop="catalog" value="red_db_local"/> <!-- this should beat 'qa_db' from ../globals/item[@grp='db' and prop='catalog'] -->
      <item grp="db" prop="passwd" value="qa_passwd"/>
      <item grp="log" prop="level" value="critical"/>
    </locals>
  </group>
  <group name="prod">
    <globals>
      <item grp="db" prop="server" value="prod_sql_1"/>
    </globals>
    <locals>
      <item grp="db" prop="catalog" value="prod_db_local"/>
      <item grp="db" prop="passwd" value="prod_passwd"/>
      <item grp="log" prop="level" value="critical"/>
    </locals>
  </group>
</config>

以及作为可用环境之一的参数,我想最终得到一个合并和重复数据删除的节点集,保留最具体的值。因此,对于“产品”:

<config>
  <item grp="db" prop="userid" value="foo"/>
  <item grp="log" prop="filename" value="red.log"/>
  <item grp="db" prop="server" value="prod_sql_1"/>
  <item grp="db" prop="catalog" value="prod_db_local"/>
  <item grp="db" prop="passwd" value="prod_passwd"/>
  <item grp="log" prop="level" value="critical"/>
</config>

我对在 XSLT 1.0 中使用键非常陌生,我想出了这个适用于“prod”的样式表,但不适用于“dev”或“qa”:

<?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" indent="yes" omit-xml-declaration="yes"/>
  <xsl:param name="environment"/>

  <!-- 
    using | to create a union of top-level global items and and env-specific items
  -->
  <xsl:variable name="all-items"
                select="/config/group[@name='global']/globals/item |
                        //group[@name=$environment]//item"/>

  <xsl:key name="dupes" match="item" use="concat(@grp,'|',@prop)"/>

  <xsl:template match="/config">
    <xsl:copy>
      <xsl:copy-of
          select="$all-items[generate-id() = generate-id(key('dupes',
                    concat(@grp,'|',@prop))[last()])]"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

这是我的目标方法:

  1. 使用联合将所有相关<item.../>节点合并到节点集中|
  2. @grp按和@prop属性对这个节点集进行分组
  3. 将最后一个节点保留在任何结果组中(重复数据删除)

由于我是键新手,我只能说我认为这段代码,

<xsl:copy-of select="$all-items[generate-id() = generate-id(key('dupes',
                                            concat(@grp,'|',@prop))[last()])]"/>

正在last()从重复项的节点集中选择节点,但是当使用“dev”或“qa”运行时,我得到以下信息:

REG zacharyyoung$ xsltproc --stringparam environment dev config3.xsl config3.xml 
<config>
  <item grp="db" prop="userid" value="foo"/>
  <item grp="log" prop="filename" value="red.log"/>
</config>
REG zacharyyoung$ xsltproc --stringparam environment qa config3.xsl config3.xml
<config>
  <item grp="db" prop="userid" value="foo"/>
  <item grp="log" prop="filename" value="red.log"/>
</config>

我检查了all-items每个环境参数的中间变量,看来至少有那么多工作正常。

如果我移动<group name="qa"/>到底部,例如:

<config>
  <group name="global">...</group>
  <group name="dev">...</group>
  <group name="prod">...</group>
  <group name="qa">...</group>
<config>

然后用'qa'运行它:

REG zacharyyoung$ xsltproc --stringparam environment qa config3.xsl config3.xml
<config>
  <item grp="db" prop="userid" value="foo"/>
  <item grp="log" prop="filename" value="red.log"/>
  <item grp="db" prop="server" value="qa_sql_1"/>
  <item grp="db" prop="catalog" value="red_db_local"/>
  <item grp="db" prop="passwd" value="qa_passwd"/>
  <item grp="log" prop="level" value="critical"/>
</config>

那么,为什么<group name="...">...</group>我选择的位置很重要?具体来说,为什么它只在最后一个位置起作用,我如何使它适用于任何位置?

编辑 1

当我从(对于任何环境)中隔离数据$all-items并将其放入自己的文件中时,XSL 可以正常工作。以下示例是全局变量和“开发”的联合:

<config>
  <item grp="db" prop="userid" value="foo"/>
  <item grp="db" prop="passwd" value="bar"/>
  <item grp="log" prop="level" value="debug"/>
  <item grp="log" prop="filename" value="red.log"/>
  <item grp="db" prop="server" value="dev_sql_1"/>
  <item grp="db" prop="catalog" value="red_db_local"/>
  <item grp="db" prop="passwd" value="dev_passwd"/>
  <item grp="log" prop="level" value="info"/>
</config>

这个 XSL:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
  <xsl:key name="dupes" match="item" use="concat(@grp,'|',@prop)"/>
  <xsl:template match="/config">
    <xsl:copy>
          <xsl:copy-of
              select="item[generate-id() = generate-id(key('dupes',
                      concat(@grp,'|',@prop))[last()])]"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

结果是:

REG zacharyyoung$ xsltproc config4.xsl config4.xml
<config>
  <item grp="db" prop="userid" value="foo"/>
  <item grp="log" prop="filename" value="red.log"/>
  <item grp="db" prop="server" value="dev_sql_1"/>
  <item grp="db" prop="catalog" value="red_db_local"/>
  <item grp="db" prop="passwd" value="dev_passwd"/>
  <item grp="log" prop="level" value="info"/>
</config>

那么,现在它似乎取决于变量all-items

谢谢你。

4

2 回答 2

1

我不确定为什么分组不起作用(我会尽快查看),但您也可以完全不使用键来实现您想要的输出。

这个 XSLT 1.0 样式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:param name="environment" select="'qa'"/>

  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="group">
    <xsl:if test="@name = $environment">
      <xsl:apply-templates select="/config/group[@name='global']/globals/item[not(@prop = /config/group[@name='prod']/locals/item/@prop)]"/>
      <xsl:apply-templates/>      
    </xsl:if>
  </xsl:template>

  <xsl:template match="globals|locals">
    <xsl:apply-templates/>
  </xsl:template>

</xsl:stylesheet>

应用于您的输入 XML 会产生所需的输出:

<config>
   <item grp="db" prop="userid" value="foo"/>
   <item grp="log" prop="filename" value="red.log"/>
   <item grp="db" prop="server" value="qa_sql_1"/>
   <item grp="db" prop="catalog" value="red_db_local"/>
   <item grp="db" prop="passwd" value="qa_passwd"/>
   <item grp="log" prop="level" value="critical"/>
</config>

这也适用于“prod”和“dev”。

编辑:从谓词中删除变量。

于 2012-01-16T21:55:32.407 回答
0

离开DevNull 的回答(回答了所述问题),我做了以下修改(对于一些未说明的要求):

  1. 添加了要混合的环境全局变量,以防在此处输入重复值
  2. 删除了身份转换,因为它似乎不符合我的需要

这是完整的样式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:param name="environment" select="'qa'"/>

  <xsl:template match="/config">
    <xsl:copy>
      <xsl:copy-of select="/config/group[@name='global']/globals/item[not(@prop = /config/group[@name=$environment]//item/@prop)]"/>
      <xsl:copy-of select="/config/group[@name=$environment]/globals/item[not(@prop = /config/group[@name=$environment]/locals/item/@prop)]"/>
      <xsl:copy-of select="/config/group[@name=$environment]/locals/item"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

我仍然希望找出我原来问题中的键有什么问题。

于 2012-01-16T22:45:44.077 回答