平面文件到XML分组

时间:2014-03-27 18:33:49

标签: xml xslt grouping

我有一个平面文件,我想使用XSLT转换为XML。 每行的第一个字符代表一个信息块,我想将所有内容组合在一起。 这些行可以以多个字符开头。我想要做的是将字符块组合在一起,它们位于字符1之间。

这是输入文件的样子:

0xxxxxxxxxxxxxxxxxxxxxxxxx
1xxxxxxxxxxxxxxxxxxxxxxxxx
2xxxxxxxxxxxxxxxxxxxxxxxxx
3xxxxxxxxxxxxxxxxxxxxxxxxx
5xxxxxxxxxxxxxxxxxxxxxxxxx
8xxxxxxxxxxxxxxxxxxxxxxxxx
1xxxxxxxxxxxxxxxxxxxxxxxxx
2xxxxxxxxxxxxxxxxxxxxxxxxx
5xxxxxxxxxxxxxxxxxxxxxxxxx
8xxxxxxxxxxxxxxxxxxxxxxxxx
1xxxxxxxxxxxxxxxxxxxxxxxxx
8xxxxxxxxxxxxxxxxxxxxxxxxx
9xxxxxxxxxxxxxxxxxxxxxxxxx

x只表示我可以处理的行中的数据。 我想要做的就是产品:

<Root>
    <Header> // O line
    </Header>
    <Summary id="xxxxx"> // First 1 line
        <data_from_2>
        </data_from_2>
        <data_from_3>
        </data_from_3>
        <data_from_5>
        </data_from_5>
        <data_from_8>
        </data_from_8>
    </Summary>
    <Summary id="xxxxx"> // Second 1 line
        <data_from_2>
        </data_from_2>
        <data_from_3>
        </data_from_3>
        <data_from_5>
        </data_from_5>
        <data_from_8>
        </data_from_8>
    </Summary>
    <Summary id="xxxxx"> // Third 1 line
        <data_from_2>
        </data_from_2>
        <data_from_3>
        </data_from_3>
        <data_from_5>
        </data_from_5>
        <data_from_8>
        </data_from_8>
    </Summary>
    <Footer> // 9 line
    </Footer>
</Root>

困难的部分是不知道1行下会有多少行。 可能只有一行可以分组或更多行。

这是我最初的XSLT(它目前产生一个扁平结构):

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="2.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:variable name="newline" select="'&#x0A;'" />
    <xsl:variable name="tab" select="'&#x09;'" />

        <xsl:template match="/">
            <xsl:value-of select="$newline"/>
            <FirstData>
            <xsl:value-of select="$newline"/>

                <xsl:for-each select="tokenize(.,'\r?\n')">
                    <!-- DETERMINE WHAT FIRST CHAR LOOKS LIKE -->
                    <xsl:variable name="lineToken" select="substring(., 1, 1)"/>

                    <!-- HEADER -->
                    <xsl:if test="$lineToken='0'">
                        <xsl:variable name="periodStart" select="substring(., 2, 6)"/>
                        <xsl:value-of select="$tab"/><HEADER><xsl:value-of select="$newline"/>
                            <xsl:value-of select="$tab"/><xsl:value-of select="$tab"/><Period_start_date><xsl:sequence select="$periodStart"/></Period_start_date><xsl:value-of select="$newline"/>
                        <xsl:value-of select="$tab"/></HEADER><xsl:value-of select="$newline"/>
                    </xsl:if>


                    <!-- SUMMARY -->
                    <xsl:if test="$lineToken='1'">
                        <xsl:value-of select="$tab"/><xsl:element name="SUMMARY">
                        <xsl:attribute name="ID"><xsl:value-of select ="substring(., 2, 11)"/></xsl:attribute>
                        <xsl:value-of select="$newline"/>
                            <xsl:variable name="ID" select="substring(., 2, 11)"/>
                            <xsl:variable name="batchDate" select="substring(., 13, 4)"/>
                            <xsl:value-of select="$tab"/><xsl:value-of select="$tab"/><ID><xsl:sequence select="$fdmsMerchantNum"/></FDMS_Merchant_Number><xsl:value-of select="$newline"/>
                            <xsl:value-of select="$tab"/><xsl:value-of select="$tab"/><Batch_Date><xsl:sequence select="$batchDate"/></Batch_Date><xsl:value-of select="$newline"/>
                        <xsl:value-of select="$tab"/></xsl:element><xsl:value-of select="$newline"/>
                    </xsl:if>

                    <!-- Data 2 -->
                    <xsl:if test="$lineToken='2'">
                        <xsl:value-of select="$tab"/><Data_2><xsl:value-of select="$newline"/>
                            <xsl:variable name="Sales" select="substring(., 2, 3)"/>
                            <xsl:value-of select="$tab"/><xsl:value-of select="$tab"/><Sales><xsl:sequence select="$Sales"/></Sales><xsl:value-of select="$newline"/>
                        <xsl:value-of select="$tab"/></Data_2><xsl:value-of select="$newline"/>
                    </xsl:if>
                    <!-- Data 3 -->
                    <xsl:if test="$lineToken='3'">
                        <xsl:value-of select="$tab"/><Data_3><xsl:value-of select="$newline"/>
                            <xsl:variable name="Sales" select="substring(., 2, 3)"/>
                            <xsl:value-of select="$tab"/><xsl:value-of select="$tab"/><Sales><xsl:sequence select="$Sales"/></Sales><xsl:value-of select="$newline"/>
                        <xsl:value-of select="$tab"/></Data_3><xsl:value-of select="$newline"/>
                    </xsl:if>
                            <!-- Data 5 and Data 8 elements are identical -->
                </xsl:for-each>
            </Root>
        </xsl:template>
    </xsl:stylesheet>

我想要做的是能够将数据2和数据3元素嵌套在摘要元素中,但是如何处理这些行然后为下一个遇到的1行开始一个新的摘要元素?

我很抱歉所有的通用内容,我有很多数据可以使用,我正在尝试简化问题。 如果需要更多信息,请告诉我。

1 个答案:

答案 0 :(得分:1)

它看起来像for-each-group group-starting-with的作业,但在XSLT 2.0中,您只能将其用于节点序列,而不能用于字符串序列。所以我首先将你从tokenize(.,'\r?\n')获得的行包装成一个元素,例如

<xsl:variable name="lines" as="element(line)*">
  <xsl:for-each select="tokenize(.,'\r?\n')">
    <line><xsl:value-of select="."/></line>
  </xsl:for-each>
</xsl:variable>

然后我会用

<xsl:for-each-group select="$lines" group-starting-with="line[starts-with(., '1')]">
  <xsl:choose>
    <xsl:when test="not(self::line[starts-with(., '1')])">
      <!-- header -->
      <Header><xsl:value-of select="substring(., 2)"/></Header>
    </xsl:when>
    <xsl:otherwise>
      <Summary id="{substring(., 2)}">
         <!-- now use for-each select="if (position() eq last()) then current-group()[position() gt 1 and position() ne last()] else current-group()[position() gt 1]" or apply-templates to output the lines-->
        <xsl:for-each select="if (position() eq last()) then current-group()[position() gt 1 and position() ne last()] else current-group()[position() gt 1]">
          <xsl:element name="data_from_{substring(., 1, 1)}"><xsl:value-of select="substring(., 2)"/></xsl:element>
         </xsl:for-each>
      </Summary>
      <xsl:if test="position() eq last()">
        <Footer>
          <xsl:value-of select="substring(current-group()[last()], 2)"/>
        </Footer>
      </xsl:if>
   </xsl:otherwise>
 </xsl:choose>
</xsl:for-each-group>

分组。

我现在已经找到了一些时间来编写一个工作样本,XSLT是

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="2.0">

<xsl:param name="text-url" select="'test2014032901.txt'"/>

<xsl:output indent="yes"/>

<xsl:template name="main">
  <xsl:variable name="text" select="unparsed-text($text-url)"/>

  <xsl:variable name="lines" as="element(line)*">
    <xsl:for-each select="tokenize($text,'\r?\n')[normalize-space()]">
      <line><xsl:value-of select="."/></line>
    </xsl:for-each>
  </xsl:variable>

  <Root>
    <xsl:for-each-group select="$lines" group-starting-with="line[starts-with(., '1')]">
      <xsl:choose>
        <xsl:when test="not(self::line[starts-with(., '1')])">
          <!-- header -->
          <xsl:variable name="periodStart" select="substring(., 2, 6)"/>
          <Header>
            <Period_start_date>
              <xsl:value-of select="$periodStart"/>
            </Period_start_date>
          </Header>
        </xsl:when>
        <xsl:otherwise>
          <Summary id="{substring(., 2, 11)}">
            <ID><xsl:value-of select="substring(., 2, 11)"/></ID>
            <Batch_Date><xsl:value-of select="substring(., 13, 4)"/></Batch_Date>
            <!-- now use for-each select="if (position() eq last()) then current-group()[position() gt 1 and position() ne last()] else current-group()[position() gt 1]" or apply-templates to output the lines-->
            <xsl:for-each select="if (position() eq last()) then current-group()[position() gt 1 and position() ne last()] else current-group()[position() gt 1]">
              <xsl:element name="data_from_{substring(., 1, 1)}">
                <Sales>
                  <xsl:value-of select="substring(., 2, 3)"/>
                </Sales>
              </xsl:element>
            </xsl:for-each>
          </Summary>
          <xsl:if test="position() eq last()">
            <Footer>
              <xsl:value-of select="substring(current-group()[last()], 2)"/>
            </Footer>
          </xsl:if>
       </xsl:otherwise>
     </xsl:choose>
    </xsl:for-each-group>
  </Root>

</xsl:template>

</xsl:stylesheet>

纯文本文件的名称作为参数text-url传入,样式表应该以{{1​​}}(Saxon的名为it:main的模板)开始,然后我得到结果

main