合并和重命名xslt中的字段

时间:2011-02-02 20:51:15

标签: xslt

我是xslt的新手,我担心这个问题取决于我是否会陷入程序化的思维模式。

我有一组我要重命名的信息,在某些情况下,还有一些信息。我可以处理重命名,但是冷凝让我感到高兴。我的方法是重命名所有内容,然后,如果重命名产生重复,则删除重复项。为了清楚起见,这里有一些代码。

输入:

<variables> 
    <var value="a-value"> a </var>
    <var value="b-value"> b </var>
    <var value="c-value"> c </var>
    <var value="d-value"> d </var>
</variables>

所需的输出:

<variables> 
    <var value="new-a-value"> new-a </var>
    <var value="new-b-and-c-value"> new-b-and-c </var>
    <var value="new-d-value"> new-d </var>
</variables>

我的xslt:

  <xsl:for-each select="var">
    <xsl:choose>

      <xsl:when test="@value = 'a-value'">
        <var value="new-a-value"> new-a </var>
      </xsl:when>

      <xsl:when test="@value = 'b-value'">
        <var value="new-b-and-c-value"> new-b-and-c </var>
      </xsl:when>

      <xsl:when test="@value = 'c-value'">
        <var value="new-b-and-c-value"> new-b-and-c </var>
      </xsl:when>

      <xsl:when test="@value = 'd-value'">
        <var value="new-d-value"> new-d </var>
      </xsl:when>

    </xsl:choose>
  </xsl:for-each>

我想在重命名后删除重复的条款。我不知道从哪里开始。我的直觉促使我想要在找到b值或c值时设置一些检查,或者通过中间处理的xml运行另一次迭代来删除重复项,但我不认为这两种方法都是在xslt中可行。

3 个答案:

答案 0 :(得分:3)

重构:更简单,让真正的问题变得清晰:对映射的键值进行分组。

此样式表:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:v="value"
 exclude-result-prefixes="v">
    <xsl:key name="kVarByReplace"
             match="var"
             use="document('')/*/v:v[@o=current()/@value]/@n"/>
    <v:v o="a-value" n="new-a-value"/>
    <v:v o="b-value" n="new-b-and-c-value"/>
    <v:v o="c-value" n="new-b-and-c-value"/>
    <v:v o="d-value" n="new-d-value"/>
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="var">
        <xsl:variable name="vKey"
                      select="document('')/*/v:v[@o=current()/@value]/@n"/>
        <xsl:if test="count(.|key('kVarByReplace',$vKey)[1]) = 1">
            <var value="{$vKey}">
                <xsl:value-of select="$vKey"/>
            </var>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

输出:

<variables>
    <var value="new-a-value">new-a-value</var>
    <var value="new-b-and-c-value">new-b-and-c-value</var>
    <var value="new-d-value">new-d-value</var>
</variables>

注意:在document()中使用xsl:key/@use功能。

这表明这在XSLT 2.0中是多么容易:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:variable name="vMap" as="element()*">
        <v o="a-value">new-a-value</v>
        <v o="b-value">new-b-and-c-value</v>
        <v o="c-value">new-b-and-c-value</v>
        <v o="d-value">new-d-value</v>
    </xsl:variable>
    <xsl:template match="variables">
        <variables>
            <xsl:for-each-group select="var"
                                group-by="$vMap[@o=current()/@value]">
                <var value="{current-grouping-key()}">
                    <xsl:value-of select="current-grouping-key()"/>
                </var>
            </xsl:for-each-group>
        </variables>
    </xsl:template>
</xsl:stylesheet>

第一个解决方案,也为地图提供了一个键。

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:v="value"
 exclude-result-prefixes="v">
    <xsl:key name="kValByOld" match="v:v" use="@o"/>
    <xsl:key name="kVarByReplace"
             match="var"
             use="document('')/*/v:v[@o=current()/@value]/@n"/>
    <xsl:variable name="vStylesheet" select="document('')"/>
    <v:v o="a-value" n="new-a-value"/>
    <v:v o="b-value" n="new-b-and-c-value"/>
    <v:v o="c-value" n="new-b-and-c-value"/>
    <v:v o="d-value" n="new-d-value"/>
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="var">
        <xsl:variable name="vOld" select="@value"/>
        <xsl:for-each select="$vStylesheet">
            <xsl:variable name="vNew"
                          select="key('kValByOld',$vOld)/@n"/>
            <xsl:for-each select="$vOld">
                <xsl:if test="count(..|key('kVarByReplace',$vNew)[1])=1">
                    <var value="{$vNew}">
                        <xsl:value-of select="$vNew"/>
                    </var>
                </xsl:if>
            </xsl:for-each>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

答案 1 :(得分:2)

这是一个XSLT 2.0样式表,可以从输入中生成所需的输出。它是否与其他输入做对是另一个问题,但它的设计是适当的可扩展性,并演示了可用于此类问题的各种技术:

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:f="http://local-functions/"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
 exclude-result-prefixes="f xs">

<xsl:output indent="yes"/>

<xsl:template match="variables">
  <variable>
     <xsl:for-each-group select="*" group-adjacent="f:is-groupable(.)">
        <xsl:choose>
           <xsl:when test="f:is-groupable(.)">
              <var value="new-{string-join(current-group()/replace(@value, '-value', ''), '-and-')}-value">
                 <xsl:text> new-</xsl:text>
                 <xsl:value-of select="string-join(current-group()/normalize-space(.), '-and-')"/>
                 <xsl:text> </xsl:text>
              </var>
           </xsl:when>
           <xsl:otherwise>
              <xsl:for-each select="current-group()">
                <var value="new-{@value}">
                   <xsl:value-of select="replace(., '\S+', 'new-$0')"/>
                </var>
              </xsl:for-each>      
           </xsl:otherwise>
        </xsl:choose>
     </xsl:for-each-group>
  </variable>
</xsl:template>

<xsl:function name="f:is-groupable" as="xs:boolean">
    <xsl:param name="var" as="element(var)"/>
    <xsl:sequence select="$var/@value = ('b-value', 'c-value')"/>
</xsl:function>     

</xsl:stylesheet>

答案 2 :(得分:1)

关于“通过中间处理的xml运行另一次迭代将删除重复项” - 这可以在XSLT 2.0中实现,或者在XSLT 1.0中具有nodeset扩展。但在这种情况下,我不认为这是必要的。

而不是for-each和select-when-test-否则,请执行apply-templates:

<xsl:apply-templates select="var" />

然后你有一个模板用于你想要处理的每种类型的var,大致对应于你的xsl:when elements:

<xsl:template match="var[@value = 'a-value']">
    <var value="new-a-value"> new-a </var>
</xsl:template>

对于b / c项目,例如:

<xsl:template match="var[@value = 'b-value' or @value = 'c-value'][1]">
    <var value="new-b-and-c-value"> new-b-and-c </var>
</xsl:template>

[1]使模板仅针对其兄弟姐妹中的第一个此类变量触发。这是否正是您想要的,取决于您的输入结构可能会有所不同。 但是考虑到你的样本输入,它是有效的。您还需要一个模板来处理不是第一个的b或c变量:

<xsl:template match="var[@value = 'b-value' or @value = 'c-value']"
        priority="-1" />

注意空体;和低优先级,以便 的b / c变量首先不被此模板处理。

总结一下,根据您的示例输入,以下XSLT样式表提供了您请求的输出:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   version="1.0">
   <xsl:output method="xml" indent="yes"/>

   <xsl:template match="/variables">
      <xsl:copy>
         <xsl:apply-templates select="var"/>
      </xsl:copy>
   </xsl:template>

   <xsl:template match="var[@value = 'a-value']">
      <var value="new-a-value"> new-a </var>
   </xsl:template>

   <xsl:template match="var[@value = 'b-value' or @value = 'c-value'][1]">
      <var value="new-b-and-c-value"> new-b-and-c </var>
   </xsl:template>

   <xsl:template match="var[@value = 'b-value' or @value = 'c-value']"
        priority="-1" />

   <xsl:template match="var[@value = 'd-value']">
      <var value="new-d-value"> new-d </var>
   </xsl:template>
</xsl:stylesheet>

如果这与您的想法不符,请告诉我,我们可以进行迭代。

如果你知道输入中只有一个'b',你可以变得更简单,只需要一个模板来匹配'b'并用新值替换它;并有一个空模板来匹配'c'并扔掉它。但听起来好像你不能做出这样的假设......