xslt1.0(firefox):根据存在的不同值的总数重新格式化xml代码



我真的认为这是不可能通过XSLT解决的,所以我将不得不用JS做一些事情,或者干脆不实现。但在放弃之前,我当然必须在这里发帖,看看我是否错了,XSLT是否可以做这种逻辑。

编辑:现在我开始看到这是可能的,越来越接近

第2版:我必须更正提供的XML代码中的一个错误。该解决方案应该能够处理具有相同值的多个节点。

其概念是,我需要重新格式化代码,包括反映不同值总数的空标记。很难解释,看代码更容易理解。

初始XML代码

<data>
    <prot seq="AAA">
        <node num="4">1345</node>
    </prot>
    <prot seq="BBB">
        <node num="7">6666</node>
    </prot>
    <prot seq="CCC">
        <node num="10">3e33</node>
    </prot>
    <prot seq="DDD">
        <node num="4">1345</node>
    </prot>
    <prot seq="EEE">
        <node num="10">3e33</node>
    </prot>
</data>

并且希望输出

<root>
    <prot seq="AAA">
        <node num="4">1345</node><node num="7">-</node><node num="10">-</node>
    </prot>
    <prot seq="BBB">
        <node num="4">-</node><node num="7">6666</node><node num="10">-</node>
    </prot>
    <prot seq="CCC">
        <node num="4">-</node><node num="7">-</node><node num="10">3e33</node>
    </prot>
    <prot seq="DDD">
        <node num="4">1345</node><node num="7">-</node><node num="10">-</node>
    </prot>
    <prot seq="EEE">
        <node num="4">-</node><node num="7">-</node><node num="10">3e33</node>
    </prot>
</root>

知道吗?

谢谢!

编辑3:从Dimitri解决方案,我最终得到了这个简化的解决方案

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kNodeByNum" match="/data/prot/node" use="@num"/>
<xsl:template match="/">
    <root>
        <xsl:apply-templates select="/data/prot"/>
    </root>
</xsl:template>
<xsl:template match="/data/prot">
  <xsl:variable name="current_num" select="node/@num"/>
  <xsl:variable name="current_value" select="node"/>
  <prot seq="{@seq}">
    <xsl:for-each select="/data/prot/node[
    generate-id()
    =
    generate-id(key('kNodeByNum', @num)[1])
    ]">
      <xsl:choose>
        <xsl:when test="@num = $current_num">
          <node num="{@num}"><xsl:value-of select="$current_value"/></node>
        </xsl:when>
        <xsl:otherwise>
          <node num="{@num}">-</node>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:for-each>
  </prot>
</xsl:template>
</xsl:stylesheet>

然而,这段代码也无法处理firefox中的节点数量,并且会进入一个永远类似的循环(我必须强制关闭firefox)。

但我认为这与节点数量无关,而是代码(?)

有问题

我还没有在FireFox中测试过,但它在Xalan和Saxon中有效。。。

编辑:在FireFox8.0中进行测试,并在样式表中添加注释

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>
  <!--Identity Template - Anything not matched by another template will be 
  copied without changes.-->
  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>
  <!--Change the name of the "data" element to "root" element.-->
  <xsl:template match="data">
    <root>
      <xsl:apply-templates/>
    </root>
  </xsl:template>
  <xsl:template match="prot">
    <!--Save the current "num" attribute from the child "node" element.-->
    <xsl:variable name="vCurrNum" select="node/@num"/>    
    <prot>
      <!--This will pass all attributes of "prot" through the identity template.-->
      <xsl:apply-templates select="@*"/>
      <!--This will process the the current "node" element and also any "node" elements in other
      "prot" elements that don't have the same "num" attribute as the current "node".-->
      <xsl:apply-templates select="node|/*/prot/node[@num != $vCurrNum]">
        <!--This passes the current "num" attribute to the "node" template that gets matched below.-->
        <xsl:with-param name="pNum" select="$vCurrNum"/>
        <!--This will sort the "node" elements based on their "num" attribute.-->
        <xsl:sort select="@num" data-type="number" order="ascending"/>
      </xsl:apply-templates>
    </prot>
  </xsl:template>
  <xsl:template match="node">
    <!--This is the "num" attribute value passed in the xsl:apply-templates above.-->
    <xsl:param name="pNum"/>
      <!--This will pass all attributes of "node" through the identity template.-->
      <xsl:choose>
        <!--If the current "num" attribute is the same as the "num" attribute passed to 
        the xsl:apply-templates ($pNum), output the value.-->
        <xsl:when test="@num = $pNum">          
          <node>
            <xsl:apply-templates select="@*"/>
            <xsl:apply-templates/>  
          </node>          
        </xsl:when>
        <!--This ensures we only get one of the other "node" elements. (The first "node" with that "num".)-->
        <xsl:when test="not(parent::prot[preceding-sibling::prot[node[@num = current()/@num]]])">
          <node>
            <xsl:apply-templates select="@*"/>
            <xsl:text>-</xsl:text>            
          </node>
        </xsl:when>
        <!--Ignore any other repeated node/num combinations.-->
        <xsl:otherwise/>                
      </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

使用XML输入,它生成所需的输出:

<root>
   <prot seq="AAA">
      <node num="4">1345</node>
      <node num="7">-</node>
      <node num="10">-</node>
   </prot>
   <prot seq="BBB">
      <node num="4">-</node>
      <node num="7">6666</node>
      <node num="10">-</node>
   </prot>
   <prot seq="CCC">
      <node num="4">-</node>
      <node num="7">-</node>
      <node num="10">3e33</node>
   </prot>
</root>

这里有一个简短而简单的(无xsl:choosexsl:whenxsl:otherwise)解决方案

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>
 <xsl:template match="node()|@*">
  <xsl:param name="pParent" select="/.."/>
  <xsl:copy>
   <xsl:apply-templates select="node()|@*">
    <xsl:with-param name="pParent" select="$pParent"/>
   </xsl:apply-templates>
  </xsl:copy>
 </xsl:template>
 <xsl:template match="prot">
  <xsl:copy>
   <xsl:copy-of select="@*"/>
   <xsl:apply-templates select="../prot/node">
    <xsl:with-param name="pParent" select="."/>
   </xsl:apply-templates>
  </xsl:copy>
 </xsl:template>
 <xsl:template match="node/text()">
  <xsl:param name="pParent" select="/.."/>
  <xsl:variable name="vSameParent" select=
  "boolean(not((../.. | $pParent)[2]))"/>
  <xsl:value-of select=
  "concat(substring('-', 1 +$vSameParent),
                    self::node()[$vSameParent]
                   )
  "/>
 </xsl:template>
</xsl:stylesheet>

当此转换应用于所提供的XML文档时

<data>
    <prot seq="AAA">
        <node num="4">1345</node>
    </prot>
    <prot seq="BBB">
        <node num="7">6666</node>
    </prot>
    <prot seq="CCC">
        <node num="10">3e33</node>
    </prot>
</data>

生成所需的正确结果

<data>
   <prot seq="AAA">
      <node num="4">1345</node>
      <node num="7">-</node>
      <node num="10">-</node>
   </prot>
   <prot seq="BBB">
      <node num="4">-</node>
      <node num="7">6666</node>
      <node num="10">-</node>
   </prot>
   <prot seq="CCC">
      <node num="4">-</node>
      <node num="7">-</node>
      <node num="10">3e33</node>
   </prot>
</data>

解释

  1. 我们使用并覆盖身份规则的修改版本——一个接受并传递名为$pParent的参数的版本。

  2. 参数$pParent包含发布xsl:apply-templatesnode元素,其中一部分是当前节点的处理。

  3. 我们有两个覆盖身份规则的模板。第一个覆盖模板匹配任何prot元素。它几乎与标识规则相同,但它为$pParent参数设置了一个有意义的值(该节点本身)。

  4. 第二个覆盖模板匹配作为任何node元素的子元素的任何文本节点。这里,根据$pParent的值是否标识匹配的文本节点的祖父母,我们分别输出文本节点的值或仅输出"-"

  5. 决定输出什么是在不使用任何显式条件指令的情况下完成的。相反,我们使用带有两个参数的XPath concat()函数,其中一个参数恰好是非空字符串。为了确保这一特性,我们使用布尔变量$vSameParent,其指定方式是,当匹配文本节点的父节点与$pParent中包含的节点相同时,其值恰好为true()。最后,我们使用这样一个事实,即当用作数字时,true()的布尔值转换为1false()的布尔值转化为0

更新:这是一个新的解决方案——针对OP问题修改的:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>
 <xsl:key name="kNodeByNum" match="node" use="@num"/>
 <xsl:template match="node()|@*">
  <xsl:param name="pNum"/>
  <xsl:copy>
   <xsl:apply-templates select="node()|@*">
    <xsl:with-param name="pNum" select="$pNum"/>
   </xsl:apply-templates>
  </xsl:copy>
 </xsl:template>
 <xsl:template match="prot">
  <xsl:copy>
   <xsl:copy-of select="@*"/>
   <xsl:apply-templates select=
   "../prot/node
            [generate-id()
            =
             generate-id(key('kNodeByNum', @num)[1])
            ]
   ">
    <xsl:with-param name="pNum" select="node/@num"/>
   </xsl:apply-templates>
  </xsl:copy>
 </xsl:template>
 <xsl:template match="node/text()">
  <xsl:param name="pNum" select="/.."/>
  <xsl:variable name="vSameNum" select=
  "../@num = $pNum"/>
  <xsl:value-of select=
  "concat(substring('-', 1 +$vSameNum),
                    self::node()[$vSameNum]
                   )
  "/>
 </xsl:template>
</xsl:stylesheet>

当此转换应用于所提供的XML文档时

<data>
    <prot seq="AAA">
        <node num="4">1345</node>
    </prot>
    <prot seq="BBB">
        <node num="7">6666</node>
    </prot>
    <prot seq="CCC">
        <node num="10">3e33</node>
    </prot>
    <prot seq="DDD">
        <node num="4">1345</node>
    </prot>
    <prot seq="EEE">
        <node num="10">3e33</node>
    </prot>
</data>

生成新的所需结果

<data>
   <prot seq="AAA">
      <node num="4">1345</node>
      <node num="7">-</node>
      <node num="10">-</node>
   </prot>
   <prot seq="BBB">
      <node num="4">-</node>
      <node num="7">6666</node>
      <node num="10">-</node>
   </prot>
   <prot seq="CCC">
      <node num="4">-</node>
      <node num="7">-</node>
      <node num="10">3e33</node>
   </prot>
   <prot seq="DDD">
      <node num="4">1345</node>
      <node num="7">-</node>
      <node num="10">-</node>
   </prot>
   <prot seq="EEE">
      <node num="4">-</node>
      <node num="7">-</node>
      <node num="10">3e33</node>
   </prot>
</data>

解释:与原始问题的主要思想相同,添加了Muenchian分组

相关内容

  • 没有找到相关文章

最新更新