从文本文件生成xml
我需要使用xslt从文本文件生成格式良好的XML结构。我是xslt转型的新手,这对我来说看起来有些挑战。 示例文本文件如下。我需要省略第一部分并将引号中的值映射到输出xml文件。从文本文件生成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>
任何人可以帮我生成此文件。提前致谢!
我会在两个阶段做这个转换(通过分解来简化任务)。第一阶段生成与原始文本同构的XML;第二阶段重构XML。
第1阶段是这样的:
<xsl:template name="main">
<doc>
<xsl:for-each select="tokenize(unparsed-text('input.txt'), '\r?\n')
[starts-with(., '"')]">
<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应用程序。
这个答案的问题是,我不认为你所做的*比equivilent python/prel/etc ...更可读,因为它们是为文件解析设计的脚本语言。 XSLT是(或应该是)关于转换XML文档。其次,通过使用XSLT,提问者现在被高度限制为基于Java或CLI的解决方案,这使得在大型系统中导入或重用此代码变得更加困难。 – 2013-06-24 23:30:02
用户要求提供XSLT 2.0解决方案,因为XSLT 2.0非常适合我很乐意提供的任务。 Java相当普遍,因此XSLT 2.0也是如此。 –
我同意文件解析是关键,但我不喜欢使用多种语言的想法。扩展模式表示法应该被指定为具有声明性方法,以便解析器将自动创建相应的XDM,然后允许XSLT或XQuery进行处理。 –
由于W3C状态:
以这种方式使用XSLT比较冗长,少reable,因为它不是设计来解析文本文件。尽管我已经包含了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
这段代码就像很多尝试从不是为工作而设计的语言编写XML的尝试一样,如果输入包含特殊字符(如&和
还要注意,W3C的引用来自市场推广,而不是来自规范本身。 XSLT 1.0规范有类似的句子,但它从2.0规范中被删除,作为语言功能增加的明确信号。 Java的营销手段说它是用来编写applet的...... –
虽然XSLT 2可以读取非XML输入,但您可能会发现使用脚本语言(如Python,Perl或Groovy)可以更轻松地完成此操作。 –
如果你想避免学习新的技巧,那可能是真的,但Python(等)代码不可能比下面显示的XSLT代码更简单。 –
@MichaelKay在一个更合适的语言中编码并不一定是正确的,我认为在W3C认为XSLT是“一种语言”时,建议使用XSLT进行某些操作(字符串处理)是不明智的转换XML文档“不是通用文件。 – 2013-06-25 02:01:45