从文本文件生成xml

时间:2013-06-24 20:39:53

标签: xpath xslt-2.0

我需要使用xslt从文本文件生成格式良好的XML结构。我是xslt转换的新手,对我来说这看起来有点挑战。 示例文本文件如下。我需要省略第一部分并将引号中的值映射到输出xml文件。

Directory:    sample/archive
Name:         20130613-T210002.TXT
---------------------------------------------------------------
"11FCK1GR0026" "G190" "FPB - OK Ship Pt" "A" "11" "XX" "02"
"11FCA1GR0034" "G980" "FPB -San Antonio" "A" "11" "XX" "02"
"11FCA1GR0034" "G160" "FPB -San Antonio" "A" "11" "XX" "02"

以下是所需的输出xml格式:

<Account>
    <Action>A</Action> <!-- 3rd element in the row-->
    <org>G190</org> <!-- 2nd element-->
    <code>11FCK1GR002611XX</code> <!--concat(1st element, 4th element, 5th element)-->  
    <FiscalYear>2013</FiscalYear> <!--calculate fiscal year from current date -->
    <Info>
        <Action>A</Action> <!-- 3rd element in the row-->
        <org>G190</org> <!-- 2nd element-->
        <code>11FCK1GR002611XX</code><!--concat(1st element, 4th element, 5th element)-->
        <Fieldlab>Acc1</Fieldlab> <!-- Static value-->
        <FieldVal>11FCK1GR0026 </FieldVal> <!-- if field order is 1, map 1st element -->
        <FieldOrd>1</FieldOrd> <!-- Static value-->
    <Info>
    <Info>
        <Action>A</Action>
        <org>G190</org> 
        <code>11FCK1GR002611XX</code>
        <Fieldlab>Acc2</Fieldlab>
        <FieldVal>11</FieldVal>  <!-- if field order is 2, map 5th element -->
        <FieldOrd>2</FieldOrd>
    <Info>
    <Info>
        <Action>A</Action> <!-- 4th element in the row-->
        <org>G190</org> <!-- 2nd element-->
        <code>11FCK1GR002611XX</code>
        <Fieldlab>Acc3</Fieldlab>
        <FieldVal>xx</FieldVal> <!-- if field order is 3, map 6th element -->
        <FieldOrd>3</FieldOrd>
    <Info>
    <Info>
        <Action>A</Action> <!-- 4th element in the row-->
        <org>G190</org> <!-- 2nd element-->
        <code>11FCK1GR002611XX</code>
        <Fieldlab>Acc4</Fieldlab>
        <FieldVal>02</FieldVal> <!-- if field order is 4, map 7th element -->
        <FieldOrd>4</FieldOrd>
    <Info>
    <Info>
        <Action>A</Action> <!-- 4th element in the row-->
        <org>G190</org> <!-- 2nd element-->
        <code>11FCK1GR002611XX</code>
        <Fieldlab>Acc5</Fieldlab>
        <FieldVal>FPB - OK Ship Pt</FieldVal> <!-- if field order is 5, map 3rd element -->
        <FieldOrd>5</FieldOrd>
    <Info>

</Account>
<Account>
    <Action>A</Action> <!-- 3rd element in the row-->
    <org>G980</org> <!-- 2nd element-->
    <code>111FCA1GR003411XX</code> <!--concat(1st element, 4th element, 5th element)--> 
    <FiscalYear>2013</FiscalYear>
    <Info>
        <Action>A</Action> <!-- 3rd element in the row-->
        <org>G190</org> <!-- 2nd element-->
        <code>111FCA1GR003411XX</code><!--concat(1st element, 4th element, 5th element)-->
        <Fieldlab>Acc1</Fieldlab>
        <FieldVal>11FCA1GR0034</FieldVal> <!-- if field order is 1, map 1st element -->
        <FieldOrd>1</FieldOrd> 
    <Info>
    <Info>
        <Action>A</Action>
        <org>G190</org> 
        <code>111FCA1GR003411XX</code>
        <Fieldlab>Acc2</Fieldlab>
        <FieldVal>11</FieldVal>  <!-- if field order is 2, map 5th element -->
        <FieldOrd>2</FieldOrd>
    <Info>
    <Info>
        <Action>A</Action> <!-- 4th element in the row-->
        <org>G190</org> <!-- 2nd element-->
        <code>111FCA1GR003411XX</code>
        <Fieldlab>Acc3</Fieldlab>
        <FieldVal>xx</FieldVal> <!-- if field order is 3, map 6th element -->
        <FieldOrd>3</FieldOrd>
    <Info>
    <Info>
        <Action>A</Action> <!-- 4th element in the row-->
        <org>G190</org> <!-- 2nd element-->
        <code>111FCA1GR003411XX</code>
        <Fieldlab>Acc4</Fieldlab>
        <FieldVal>02</FieldVal> <!-- if field order is 4, map 7th element -->
        <FieldOrd>4</FieldOrd>
    <Info>
    <Info>
        <Action>A</Action> <!-- 4th element in the row-->
        <org>G190</org> <!-- 2nd element-->
        <code>111FCA1GR003411XX</code>
        <Fieldlab>Acc5</Fieldlab>
        <FieldVal>FPB -San Antonio</FieldVal> <!-- if field order is 5, map 3rd element -->
        <FieldOrd>5</FieldOrd>
    <Info>
</Account>

有人可以帮我生成这个文件吗?提前谢谢!

2 个答案:

答案 0 :(得分:2)

我会分两个阶段进行这种转换(通过分解来简化任务)。第一阶段产生与原始文本同构的XML;第二阶段重构XML。

阶段1是这样的:

<xsl:template name="main">
<doc>
  <xsl:for-each select="tokenize(unparsed-text('input.txt'), '\r?\n')
                           [starts-with(., '&quot;')]">
    <row>
      <xsl:analyze-string select="." regex='\"[^"]*?\"'>
        <xsl:matching-substring>
          <col><xsl:value-of select="."/></col>
        </xsl:matching-substring>
      </xsl:analyze-string>
    </row>
  </xsl:for-each>
</doc>
</xsl:template>

阶段2是这样的:

<xsl:template match="doc">
  <Accounts>
    <xsl:apply-templates/>
  </Accounts>
</xsl:template>

<xsl:template match="row">
  <xsl:variable name="row" select="."/>
  <Account>
    <Action><xsl:value-of select="col[4]"/></Action>
    <org><xsl:value-of select="col[2]"/></org>
    <xsl:variable name="code" select="concat(col[1], col[4], col[5])"/>
    <code><xsl:value-of select="$code"/></code>
    <FiscalYear><xsl:value-of select="year-from-date(current-date())"/></FiscalYear>
    <xsl:for-each select="1 to 5">
      <xsl:variable name="p" select="."/>
      <Info>
        <Action><xsl:value-of select="$row/col[4]"/></Action>
        <org><xsl:value-of select="$row/col[2]"/></org>
        <code><xsl:value-of select="$code"/></code>
        <FieldLab>Acc<xsl:value-of select="."/></FieldLab>
        <FieldVal><xsl:value-of select="$row/col[(1,5,6,7,3)[$p]]"/></FieldVal>
        <FieldOrd><xsl:value-of select="."/></FieldOrd>
     </Info>
   </xsl:for-each>
  </Account>
</xsl:template> 

您可以通过多种方式将两个阶段串联起来。您可以在单个样式表中执行此操作(将第一个阶段的结果放在变量中,然后在第二个阶段处理它),或者您可以从shell脚本,Ant或XProc或xmlsh依次执行它们,或自定义Java应用程序。

答案 1 :(得分:1)

正如W3C所说:

  

XSL Transformations (XSLT 2.0) is a language for transforming XML documents into other XML documents, text documents or HTML documents

以这种方式使用XSLT更加冗长,更不可靠,因为它不是为解析文本文件而设计的。虽然我已经包含了一个Python解决方案,正如@JimGarrison指出的那样,还有很多其他字符串I / O语言更适合。除非您有强烈的业务需求,必须使用XSLT,否则我强烈建议您查看下面的代码并将其翻译成您选择的语言。

我认为您已经问过一个经典XY Problem,所以我在Python中编写了一个替代版本,您可以在此处看到:http://codepad.org/JWNbqrwD。它不包括文件I / O,但这并不是一个非常棘手的更改。

一些事情,你的输出不是XML - 没有单个根元素,并且你Info标签没有正确关闭,所以这个脚本也没有。

import re
from datetime import datetime

data = """Directory:    sample/archive
Name:         20130613-T210002.TXT
---------------------------------------------------------------
"11FCK1GR0026" "G190" "FPB - OK Ship Pt" "A" "11" "XX" "02"
"11FCA1GR0034" "G980" "FPB -San Antonio" "A" "11" "XX" "02"
"11FCA1GR0034" "G160" "FPB -San Antonio" "A" "11" "XX" "02"
"""

output = ""

# Split on new lines start at line 3.
for line in data.split("\n")[3:]:
    fields = re.findall('"([^"]*)"',line) #Find all 
    newAccount = ""
    if len(fields) == 7:
        action, org, code = fields[3],fields[2], "".join([fields[0],fields[3],fields[4]])
        newAccount = "<Account>\n" + \
            "\t<Action>%s</Action>\n" % action + \
            "\t<org>%s</org>\n" % org + \
            "\t<code>%s</code>\n" % code + \
            "\t<FiscalYear>%s</FiscalYear>\n" % datetime.now().year
        orderMap = [(1,0),(2,4),(3,5),(4,6),(5,2)]
        for pos,fieldNum in orderMap:
            newAccount += "\t<Info>\n" + \
                "\t\t<Action>%s</Action>\n" % action + \
                "\t\t<org>%s</org>\n" % org + \
                "\t\t<code>%s</code>\n" % code + \
                "\t\t<Fieldlab>Acc%s</Fieldlab>\n" % pos + \
                "\t\t<FieldVal>%s</FieldVal>\n" % fields[fieldNum]+ \
                "\t\t<FieldOrd>%s</FieldOrd>\n" % pos + \
            "\t<Info>\n"
        newAccount += "</Account>\n"

    output += newAccount

print output