(XSLT,代码优化)如何输出引用兄弟节点值的节点..?

时间:2010-02-18 10:11:38

标签: xml xslt xslt-1.0

我正在使用XSLT将XML转换为XML,目标是读取标记<node1>的值,如果它为null,则必须为其赋值<node2>,如果包含<node2> 1}},也是null,然后必须为两个标签分配默认文本“Default”..
编辑:如果<node2>为空且<node1>不为..则代码不应使用<node2>更新'Default'文本,但它必须按原样进行转换..

这是我正在尝试的测试XML:

<root>
    <node1></node1>
    <node2></node2>
  <parent>
    <node1>data1</node1>
    <node2></node2>
  </parent>
  <parent>
    <node1></node1>
    <node2>data2</node2>
  </parent>
  <parent>
    <node1>data1</node1>
    <node2>data2</node2>
  </parent>
</root>

这就是我设计的XSLT代码:

   <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>
  <xsl:template name="template1" match="node2[(following-sibling::node1[.='']|preceding-sibling::node1[.=''])]">
    <xsl:choose>
      <xsl:when test=".=''">
        <node1><xsl:text>Default</xsl:text></node1>
        <node2><xsl:text>Default</xsl:text></node2>
      </xsl:when>
      <xsl:otherwise>
        <node1>
          <xsl:value-of select="text()"/>
        </node1>
        <xsl:copy>
          <xsl:apply-templates select="node()"/>
        </xsl:copy>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <xsl:template name="template2" match="node1[.='']"/>

虽然我的代码有效,但我对代码的庞大程度感到不满。无论如何都要摆脱冗余(如果有的话)......还有其他方法可以使用2个模板来实现这一点(即template1和template2),是否可以减少模板的数量?

4 个答案:

答案 0 :(得分:3)

<强>予。 XSLT 1.0解决方案:

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
    <xsl:output omit-xml-declaration="yes" indent="yes"/>

    <xsl:variable name="vReplacement">Default</xsl:variable>

       <xsl:variable name="vRep" select=
        "document('')/*/xsl:variable[@name='vReplacement']/text()"/>

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

     <xsl:template match="node1[not(node())] | node2[../node1[not(node())]]">
      <xsl:copy>
          <xsl:copy-of select="../node2/text() | $vRep[not(current()/../node2/text())]"/>
      </xsl:copy>
     </xsl:template>
</xsl:stylesheet>

它比目前的解决方案更短更简单 - 比当前选择的解决方案少7行,更重要的是,一个模板

更重要的是,这个解决方案完全是声明式和推式 - 没有调用命名模板,唯一的<xsl:apply-templates>在身份规则中。

<强> II。 XSLT 2.0解决方案

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
    <xsl:output omit-xml-declaration="yes" indent="yes"/>

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

 <xsl:template match="node1[not(node())] | node2[../node1[not(node())]]">
  <xsl:copy>
      <xsl:sequence select="(../node2/text(), 'Default')[1]"/>
  </xsl:copy>
 </xsl:template>
</xsl:stylesheet>

利用XPath 2.0序列的强大功能,此解决方案比XSLT 1.0解决方案短得多

XSLT 1.0 中无法实现类似的功能(例如选择两个节点的并集中的第一个而不指定谓词以使两个节点互斥),因为具有默认文本的节点和node1 / node2节点属于不同的文档,正如我们所知,不同文档的节点之间的节点排序是特定于实现的,不能保证/规定。

此解决方案完全是声明性的(如果/ then / else则为no)并且完全推送样式:唯一的<xsl:apply-templates>在身份规则中。

答案 1 :(得分:2)

<xsl:stylesheet 
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
  <xsl:template match="node() | @*">
    <xsl:copy>
      <xsl:apply-templates select="node() | @*" />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="node1[.=''] | node2[.='']">
    <xsl:copy>
      <xsl:call-template name="GetOwnValue" />
    </xsl:copy>
  </xsl:template>

  <xsl:template name="GetOwnValue">
    <xsl:variable name="node2" select="following-sibling::node2[1]" />
    <xsl:choose>
      <xsl:when test="$node2 != ''">
        <xsl:value-of select="$node2" />
      </xsl:when>
      <xsl:otherwise>
        <xsl:text>Default</xsl:text>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

答案 2 :(得分:2)

我修改了Tomalak的答案并完成了要求。
正如我在我的问题中提到的,如果兄弟节点1不为空(并且如果没有兄弟节点1),则此代码将node2传递为null(如果它为null)。

这段代码最终成为了我在Q中发布的代码的替代品。(我不是说它足够完美......但我很高兴我能尝试.. :-)
这段代码比我的代码效率高出10-20毫秒......: - )

在这里......

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

  <xsl:template match="node1[.=''] | node2[.='']">
    <xsl:copy>
      <xsl:call-template name="GetOwnValue">
        <xsl:with-param name="node">
          <xsl:value-of select="name()"/>
        </xsl:with-param>
      </xsl:call-template>
    </xsl:copy>
  </xsl:template>

  <xsl:template name="GetOwnValue">
    <xsl:param name="node"/>
    <xsl:variable name="node2" select="following-sibling::node2[1]|preceding-sibling::node2[1]" />
    <xsl:variable name="node1" select="following-sibling::node1[1]|preceding-sibling::node1[1]" />
     <xsl:choose>
      <xsl:when test="$node2 != ''">
          <xsl:value-of select="$node2" />
      </xsl:when>
       <xsl:when test="$node!='node1' and ($node1!='' or not(following-sibling::node1[1]|preceding-sibling::node1[1]))"/>
      <xsl:otherwise>
          <xsl:text>Default</xsl:text>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

答案 3 :(得分:0)

使用XSLT 2.0我会这样做,但无论如何都更容易阅读。

<xsl:template match="node1[.='']">
    <xsl:copy>
        <xsl:value-of select="if (following-sibling::node2[.!='']) then following-sibling::node2[.!=''] else if (preceding-sibling::node2[.!='']) then preceding-sibling::node2[.!=''] else 'Default'"/>
    </xsl:copy>
</xsl:template>
<xsl:template match="node2[.='']">
    <xsl:copy>
        <xsl:value-of select="if (following-sibling::node1[.!='']) then '' else if (preceding-sibling::node1[.!='']) then '' else 'Default'"/>
    </xsl:copy>
</xsl:template>