0

XML:

<sample>
    <test>
        <Cell1>John</Cell1>
        <Cell2>A</Cell2>
        <Cell4>xy</Cell4>
    </test>
    <test>
        <Cell1>John</Cell1>
        <Cell2>B</Cell2>
        <Cell6>10</Cell6>
    </test>
    <test>
        <Cell1>John,Jade</Cell1>
        <Cell2>A,Y</Cell2>
        <Cell4>1</Cell4>
    </test>
    <test>
        <Cell1>John,Jade</Cell1>
        <Cell2>A C,X</Cell2>
    </test>
    <test>
        <Cell1>John,Jade</Cell1>
        <Cell2>C D,Y</Cell2>
    </test>
    <test>
        <Cell1>John</Cell1>
        <Cell2>A B</Cell2>
        <Cell4>xy</Cell4>
    </test>
</sample>

XSLT:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs">
    <xsl:output method="xml" encoding="UTF-8" indent="no"/>
    <xsl:template match="/">
        <xsl:apply-templates select="sample"/>
    </xsl:template>
    <xsl:template match="sample">
        <xsl:variable name="atomictest">
            <!--Store the test containing only one value in cell2-->
            <xsl:copy-of select="test[not(contains(Cell2,',')) or not(contains(Cell2,' '))]"/>
        </xsl:variable>
        <xsl:variable name="copy">
            <xsl:apply-templates select="test">
                <xsl:with-param name="atomictest" select="$atomictest"/>
            </xsl:apply-templates>
        </xsl:variable>
    </xsl:template>
    <xsl:template match="test">
        <xsl:param name="atomictest"/>
        <xsl:choose>
            <xsl:when test="contains(Cell2,',')">
                <xsl:variable name="Cell1">
                    <xsl:copy-of select="Cell1"/>
                </xsl:variable>
                <!-- tokenize cell2 based on comma -->
                <xsl:for-each select="tokenize(Cell2,',')">
                    <xsl:variable name="str">
                        <xsl:value-of select="."/>
                    </xsl:variable>
                    <xsl:variable name="pos">
                        <xsl:value-of select="position()"/>
                    </xsl:variable>
                    <xsl:choose>
                        <!-- If cell2 contains space -->
                        <xsl:when test="contains(.,' ')">
                            <!-- tokenize cell2 based on comma -->
                            <xsl:for-each select="tokenize(.,' ')">
                                <xsl:variable name="str">
                                    <xsl:value-of select="."/>
                                </xsl:variable>
                                <!-- if cell2 value not contained in the atomic collected -->
                                <xsl:if test="not($atomictest/test[normalize-space(Cell2/text())=normalize-space($str)])">
                                    <!--Store Cell2 value -->
                                    <xsl:variable name="Cell2">
                                        <xsl:value-of select="."/>
                                    </xsl:variable>
                                    <!-- tokenize cell1-->
                                    <xsl:for-each select="tokenize($Cell1/Cell1,',')">
                                        <xsl:if test="position()=$pos">
                                            <test>
                                                <Cell1>
                                                    <xsl:value-of select="."/>
                                                </Cell1>
                                                <Cell2>
                                                    <xsl:value-of select="$Cell2"/>
                                                </Cell2>
                                            </test>
                                        </xsl:if>
                                    </xsl:for-each>
                                </xsl:if>
                            </xsl:for-each>
                        </xsl:when>
                        <xsl:otherwise>
                            <!-- if cell2 doesnot contains space -->
                            <xsl:if test="not($atomictest/test[normalize-space(Cell2/text())=normalize-space($str)])">
                                <xsl:variable name="Cell2">
                                    <xsl:value-of select="."/>
                                </xsl:variable>
                                <xsl:for-each select="tokenize($Cell1/Cell1,',')">
                                    <xsl:if test="position()=$pos">
                                        <test>
                                            <Cell1>
                                                <xsl:value-of select="."/>
                                            </Cell1>
                                            <Cell2>
                                                <xsl:value-of select="$Cell2"/>
                                            </Cell2>
                                        </test>
                                    </xsl:if>
                                </xsl:for-each>
                            </xsl:if>
                        </xsl:otherwise>
                    </xsl:choose>
                </xsl:for-each>
            </xsl:when>
            <xsl:when test="contains(Cell2,' ')">
                <xsl:variable name="Cell1">
                    <xsl:copy-of select="Cell1"/>
                </xsl:variable>
                <!-- tokenize cell2 based on space or comma -->
                <xsl:for-each select="tokenize(Cell2,' ')">
                    <xsl:variable name="str">
                        <xsl:value-of select="."/>
                    </xsl:variable>
                    <xsl:variable name="pos">
                        <xsl:value-of select="position()"/>
                    </xsl:variable>
                    <!-- if cell2 value not contained in the atomic rows collected -->
                    <xsl:if test="not($atomictest/test[normalize-space(Cell2/text())=normalize-space($str)])">
                        <xsl:if test="position()=$pos">
                            <test>
                                <Cell1>
                                    <xsl:value-of select="$Cell1"/>
                                </Cell1>
                                <Cell2>
                                    <xsl:value-of select="$str"/>
                                </Cell2>
                            </test>
                        </xsl:if>
                    </xsl:if>
                </xsl:for-each>
            </xsl:when>
            <xsl:otherwise>
                <test>
                    <Cell1>
                        <xsl:value-of select="Cell1"/>
                    </Cell1>
                    <Cell2>
                        <xsl:value-of select="Cell2"/>
                    </Cell2>
                </test>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>
  1. 我已将包含单个值的 cell2 存储在 atomictest 变量中
  2. 检查 Cell2 是否包含逗号。如果为真,则基于逗号标记 Cell2 并检查标记化的 Cell2 值是否存在于原子测试中 -> 如果没有,则将 Cell2 和 Cell1 值添加到输出
  3. 我想将输出中新添加的 Cell1 和 Cell2 值更新为 atomictest 变量,以便下次我需要跳过相同的 Cell2 值时。这该怎么做??

我得到的输出:

<test>
    <Cell1>John</Cell1>
    <Cell2>A</Cell2>
</test>
<test>
    <Cell1>John</Cell1>
    <Cell2>B</Cell2>
</test>
<test>
    <Cell1>Jade</Cell1>
    <Cell2>Y</Cell2>
</test>
<test>
    <Cell1>John</Cell1>
    <Cell2>C</Cell2>
</test>
<test>
    <Cell1>Jade</Cell1>
    <Cell2>X</Cell2>
</test>
<test>
    <Cell1>John</Cell1>
    <Cell2>C</Cell2>
</test>
<test>
    <Cell1>John</Cell1>
    <Cell2>D</Cell2>
</test>
<test>
    <Cell1>Jade</Cell1>
    <Cell2>Y</Cell2>
</test>

结果输出应如下所示:

<test>
        <Cell1>John</Cell1>
        <Cell2>A</Cell2>
    </test>
    <test>
        <Cell1>John</Cell1>
        <Cell2>B</Cell2>
    </test>
    <test>
        <Cell1>Jade</Cell1>
        <Cell2>Y</Cell2>
    </test>
    <test>
        <Cell1>John</Cell1>
        <Cell2>C</Cell2>
    </test>
    <test>
        <Cell1>Jade</Cell1>
        <Cell2>X</Cell2>
    </test>
    <test>
        <Cell1>John</Cell1>
        <Cell2>D</Cell2>
    </test>
4

2 回答 2

1

变量在 XSLT 中是只读的。也就是说,您只能将它们分配一次。之后它们是只读的。

于 2012-10-01T13:55:04.817 回答
1

这个 XSLT 2.0 样式表...

<xsl:stylesheet version="2.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:temp="http://stackoverflow.com/questions/12673307"
  exclude-result-prefixes="xsl temp">
<xsl:output omit-xml-declaration="yes" indent="yes" />
<xsl:strip-space elements="*" />  

<xsl:variable name="phase-1-output">
  <temp:tests>
    <xsl:apply-templates select="/*/test" mode="phase-1" />
  </temp:tests>
</xsl:variable>

<xsl:variable name="phase-2-output">
  <xsl:apply-templates select="$phase-1-output" mode="phase-2" />
</xsl:variable>

<xsl:template match="/">
 <xsl:copy-of select="$phase-2-output"/>
</xsl:template>

<xsl:template match="*" mode="phase-1" />

<xsl:template match="test[Cell1!=''][Cell2!='']" mode="phase-1">
  <xsl:variable name="cell2" select="tokenize(Cell2,',')" />
  <xsl:for-each select="tokenize(Cell1,',')" >
    <xsl:variable name="cell1-pos" select="position()" />
    <xsl:variable name="cell1" select="." />
    <xsl:for-each select="tokenize($cell2[$cell1-pos],' ')">
      <temp:test>
        <temp:Cell1><xsl:value-of select="$cell1" /></temp:Cell1>
        <temp:Cell2><xsl:value-of select="." /></temp:Cell2>
      </temp:test>
    </xsl:for-each>
  </xsl:for-each>
</xsl:template>

<xsl:template match="temp:tests" mode="phase-2">
  <xsl:for-each-group select="temp:test" group-by="concat(temp:Cell1,'|',temp:Cell2)">
    <test>
      <Cell1><xsl:value-of select="substring-before(current-grouping-key(),'|')" /></Cell1>
      <Cell2><xsl:value-of select="substring-after(current-grouping-key(),'|')" /></Cell2>
    </test>
  </xsl:for-each-group>
</xsl:template>

</xsl:stylesheet>

...将转换此输入...

<sample>
    <test>
        <Cell1>John</Cell1>
        <Cell2>A</Cell2>
        <Cell4>xy</Cell4>
    </test>
    <test>
        <Cell1>John</Cell1>
        <Cell2>B</Cell2>
        <Cell6>10</Cell6>
    </test>
    <test>
        <Cell1>John,Jade</Cell1>
        <Cell2>A,Y</Cell2>
        <Cell4>1</Cell4>
    </test>
    <test>
        <Cell1>John,Jade</Cell1>
        <Cell2>A C,X</Cell2>
    </test>
    <test>
        <Cell1>John,Jade</Cell1>
        <Cell2>C D,Y</Cell2>
    </test>
    <test>
        <Cell1>John</Cell1>
        <Cell2>A B</Cell2>
        <Cell4>xy</Cell4>
    </test>
</sample>

...进入...

<test>
   <Cell1>John</Cell1>
   <Cell2>A</Cell2>
</test>
<test>
   <Cell1>John</Cell1>
   <Cell2>B</Cell2>
</test>
<test>
   <Cell1>Jade</Cell1>
   <Cell2>Y</Cell2>
</test>
<test>
   <Cell1>John</Cell1>
   <Cell2>C</Cell2>
</test>
<test>
   <Cell1>Jade</Cell1>
   <Cell2>X</Cell2>
</test>
<test>
   <Cell1>John</Cell1>
   <Cell2>D</Cell2>
</test>

替代解决方案

这是另一种单相解决方案。它更简单,但适应性较差。

<xsl:stylesheet version="2.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="/">
  <xsl:for-each select="
     distinct-values(
       for $t in /*/test,
           $p1 in 1 to  count( tokenize($t/Cell1,',')),
           $cell1 in           tokenize($t/Cell1,',')[$p1],
           $cell2 in tokenize( tokenize($t/Cell2,',')[$p1], ' ') return
               concat($cell1,'|',$cell2))">
    <test>
      <Cell1><xsl:value-of select="substring-before(.,'|')" /></Cell1>
      <Cell2><xsl:value-of select="substring-after( .,'|')" /></Cell2>
    </test>
  </xsl:for-each>  
</xsl:template>

</xsl:stylesheet>

笔记

两种解决方案都依赖于以下假设:

  1. Cell1 和 Cell2 的逗号数相同。
  2. Cell1 永远不会包含竖线 ('|') 字符。
于 2012-10-01T14:31:53.200 回答