【问题标题】:(XSLT, Code optimization) How to output the nodes refering to the value of sibling-nodes ..?(XSLT,代码优化)如何输出引用兄弟节点值的节点..?
【发布时间】:2010-02-18 10:11:37
【问题描述】:

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

这是我正在尝试的测试 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),是否可以减少模板的数量?

【问题讨论】:

  • 我个人喜欢 Tomalak 的 sol .. 它满足了很多要求 .. 它看起来很酷,而且技术含量很高 .. 我可以为我的要求编写代码(可能不是 100% 有效的)但是他的代码值得.. 所以被接受.. ;-)
  • 我也发布了自己的答案..这将满足需求..
  • 我有一个答案,可能值得你关注:)
  • @Dimitre,我已经接受了你的回答..
  • 不客气——到目前为止,您一直在发布非常有趣的问题

标签: xml xslt xslt-1.0


【解决方案1】:

我。 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 行,更重要的是,少了一个模板

更重要的是,这个解决方案是完全声明式和推送式的——不调用命名模板,唯一的&lt;xsl:apply-templates&gt; 在身份规则中。

二。 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 风格:唯一的 &lt;xsl:apply-templates&gt; 在身份规则中。

【讨论】:

  • 这就是我真正想要的 .. :-)
  • @Dimitre,我需要一个关于 XSLT 1.0 解决方案的小说明,为什么我们需要再声明一个变量(即 $vRep)并分配变量 $vReplacement 的值。直接使用 $vReplacement 会出现错误(!)(我已经尝试过,很久以前)..错误背后的原因是什么?这是 XSLT 1.0 的错误吗?
  • @infant-programmer:我想将 用作 节点,而不是值,因为联合运算符“|”仅在节点上定义。在 XSLT 1.0 中,对变量的引用只为我们提供了变量的值。不幸的是,如果值 nas 嵌套子元素,它是臭名昭著的 RTF(结果树片段)类型,并且它的内部不能用 XPath 操作。有三种方法可以解决这个问题: 1. 使用 xsl:stylesheet 的命名空间子节点(具有全局范围)。 2. 使用 xsl:variable 作为节点(我在这里选择)和 3. 使用 xxx:node-set() 扩展函数。我的选择:(2)
【解决方案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>

【讨论】:

  • @Tomalak,如果 node1 不为 null 而 node2 为 null,则无需更新 node2 .. node2 必须自己转换为 null .. 你能用这个条件更新答案吗?我试过但失败了..谢谢..
  • 您应该在问题中说明此要求。
  • 好吧,除非我没有指定用默认文本更新 明确地写下这个要求..
  • 我知道 .. 但我喜欢这个解决方案,因为它满足了我的大部分要求,而且 .. 这对我来说已经足够了 .. 好吧。更新 确实没有害处,但我只是想知道代码的可能性;)
  • 我有一个答案,可能值得你关注:)
【解决方案3】:

我已经修改了Tomalak的答案并完成了要求..
正如我在我的问题中提到的,如果兄弟节点 1 不为空(并且如果没有兄弟节点 1),此代码将节点 2 传递为 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>

【讨论】:

    【解决方案4】:

    使用 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>
    

    【讨论】:

    • 我更喜欢 XSLT 1.0 .. 因为 Microsoft 产品不支持 xslt 2.0 ..thanx 无论如何都要响应 .. :-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多