4

我正在使用 XSLT 将 XML 转换为 XML,目标是读取 tag 的值<node1>,如果它为 null,则必须为其分配值<node2>,如果 incase<node2>也为 null,则必须分配默认文本“Default” .. 到两个标签 ..
编辑:如果<node2>是 null 并且<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 和模板2),是否可以减少模板的数量?

4

4 回答 4

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>的是在身份规则中。

二、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 节点属于不同的文档并且,众所周知,不同文档的节点之间的节点排序是特定于实现的,不能保证/规定。

这个解决方案是完全声明式的(没有 if/then/else)并且完全是 push 风格:唯一<xsl:apply-templates>的是在身份规则中。

于 2010-02-19T14:52:44.467 回答
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>
于 2010-02-18T10:56:41.087 回答
2

我已经修改了Tomalak的答案并完成了要求..
正如我在问题中提到的那样,如果兄弟 node1 不为空(并且如果没有兄弟 node1 ) ..

这段代码终于成为了我在 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>
于 2010-02-18T14:08:43.670 回答
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>
于 2010-02-18T10:43:09.933 回答