我需要使用xsl样式表将此xml格式化为固定宽度的文本文件。我对xsl了解甚少,在网上也找不到关于如何实现xsl的信息。
基本上我需要这个xml<?xml version="1.0" encoding="UTF-8"?>
<Report>
<table1>
<Detail_Collection>
<Detail>
<SSN>*********</SSN>
<DOB>1980/11/11</DOB>
<LastName>user</LastName>
<FirstName>test</FirstName>
<Date>2013/02/26</Date>
<Time>14233325</Time>
<CurrentStreetAddress1>53 MAIN STREET</CurrentStreetAddress1>
<CurrentCity>san diego</CurrentCity>
<CurrentState>CA</CurrentState>
</Detail_Collection>
</table1>
</Report>
在此格式中,所有在同一行
*********19801111user test 201302261423332553 MAIN STREET san diego CA
这些是固定宽度
FR TO
1 9 SSN
10 17 DOB
18 33 LastName
34 46 FirstName
47 54 Date
55 62 Time
63 90 CurrentStreetAddress1
91 115 CurrentCity
116 131 CurrentStat
非常感谢所有的帮助!提前感谢!
以下是(在我看来)更可靠和可维护的版本:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:output method="text" indent="no"/>
<xsl:variable name="some_spaces" select="' '" />
<xsl:template match="/">
<xsl:apply-templates select="//Detail_Collection/Detail" />
</xsl:template>
<xsl:template match="Detail_Collection/Detail">
<xsl:apply-templates mode="format" select="SSN">
<xsl:with-param name="width" select="number(9-1)"/>
</xsl:apply-templates>
<xsl:apply-templates mode="format_date" select="DOB">
<xsl:with-param name="width" select="number(17-10)"/>
</xsl:apply-templates>
<xsl:apply-templates mode="format" select="LastName">
<xsl:with-param name="width" select="number(33-18)"/>
</xsl:apply-templates>
<xsl:apply-templates mode="format" select="FirstName">
<xsl:with-param name="width" select="number(46-34)"/>
</xsl:apply-templates>
<xsl:apply-templates mode="format_date" select="Date">
<xsl:with-param name="width" select="number(54-47)"/>
</xsl:apply-templates>
<xsl:apply-templates mode="format" select="Time">
<xsl:with-param name="width" select="number(62-55)"/>
</xsl:apply-templates>
<xsl:apply-templates mode="format" select="CurrentStreetAddress1">
<xsl:with-param name="width" select="number(90-63)"/>
</xsl:apply-templates>
<xsl:apply-templates mode="format" select="CurrentCity">
<xsl:with-param name="width" select="number(115-91)"/>
</xsl:apply-templates>
<xsl:apply-templates mode="format" select="CurrentState">
<xsl:with-param name="width" select="number(131-116)"/>
</xsl:apply-templates>
<xsl:text> </xsl:text>
</xsl:template>
<xsl:template match="node()" mode ="format">
<xsl:param name="width" />
<xsl:value-of select="substring(concat(text(),$some_spaces ), 1, $width+1)"/>
</xsl:template>
<xsl:template match="node()" mode="format_date">
<xsl:param name="width" />
<xsl:value-of select="substring(concat(translate(text(),'/',''),$some_spaces ), 1, $width+1)"/>
</xsl:template>
</xsl:stylesheet>
即使输入中的字段与请求的输出顺序不一致,或者输入中缺少字段,它也会创建正确的输出。它还考虑有多个Detail条目
在XSLT 1.0中做到这一点的秘诀是,您可以将"填充策略"与"子字符串策略"结合起来,以填充或截断一段文本到所需的宽度。特别是这种形式的XSLT指令:
substring(concat('value to pad or cut', ' '), 1, 5)
…其中concat
用于向字符串添加一些填充字符,substring
用于限制整体宽度,这些都很有用。话虽如此,这里有一个XSLT 1.0解决方案可以实现您想要的。
请注意,在您的预期输出中,一些字符宽度不符合您的要求;例如,根据要求,<LastName>
应该被设置为16个字符,而您的输出似乎将其截断为13。也就是说,我相信我下面的解决方案会输出您所期望的结果。
当这个XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output omit-xml-declaration="no" indent="yes" method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Detail">
<xsl:apply-templates />
<xsl:text> </xsl:text>
</xsl:template>
<xsl:template match="SSN">
<xsl:value-of
select="substring(concat(., ' '), 1, 9)"/>
</xsl:template>
<xsl:template match="DOB">
<xsl:value-of
select="substring(concat(translate(., '/', ''), ' '), 1, 8)"/>
</xsl:template>
<xsl:template match="LastName">
<xsl:value-of
select="substring(concat(., ' '), 1, 16)"/>
</xsl:template>
<xsl:template match="FirstName">
<xsl:value-of
select="substring(concat(., ' '), 1, 13)"/>
</xsl:template>
<xsl:template match="Date">
<xsl:value-of
select="substring(concat(translate(., '/', ''), ' '), 1, 8)"/>
</xsl:template>
<xsl:template match="Time">
<xsl:value-of
select="substring(concat(., ' '), 1, 8)"/>
</xsl:template>
<xsl:template match="CurrentStreetAddress1">
<xsl:value-of
select="substring(concat(., ' '), 1, 28)"/>
</xsl:template>
<xsl:template match="CurrentCity">
<xsl:value-of
select="substring(concat(., ' '), 1, 25)"/>
</xsl:template>
<xsl:template match="CurrentStat">
<xsl:value-of
select="substring(concat(., ' '), 1, 15)"/>
</xsl:template>
</xsl:stylesheet>
…针对所提供的XML运行(添加了</Detail>
以使文档格式良好):
<Report>
<table1>
<Detail_Collection>
<Detail>
<SSN>*********</SSN>
<DOB>1980/11/11</DOB>
<LastName>user</LastName>
<FirstName>test</FirstName>
<Date>2013/02/26</Date>
<Time>14233325</Time>
<CurrentStreetAddress1>53 MAIN STREET</CurrentStreetAddress1>
<CurrentCity>san diego</CurrentCity>
<CurrentState>CA</CurrentState>
</Detail>
</Detail_Collection>
</table1>
</Report>
…生成所需的结果:
*********19801111user test 201302261423332553 MAIN STREET san diego CA
要在XSLT 1.0中将字符串填充到给定的长度,我将使用concat()和substring()的组合。例如,在Detail的模板中,我可以这样写
<xsl:value-of
select="substring(concat(SSN,' '),1,9)"/>
<xsl:value-of
select="substring(concat(DOB,' '),1,8)"/>
<xsl:value-of
select="substring(concat(LastName,' '),1,16)"/>
...
<xsl:text>
</xsl:text>
如果您对XSLT知之甚少,则还需要学习如何构造样式表:XSLT通常使用模板匹配来驱动样式表中的控制流,这对于来自命令式编程语言的人来说通常很难理解。
如果您知道每个Detail元素在相同的序列中都有相同的子元素(这是dtd和模式所擅长的),那么最简单的事情就是为输入中可能出现的每个元素类型编写一个模板。下面的样式表演示了部分元素的模式,但不是所有元素的模式:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:variable name="blanks10" select=" "/>
<xsl:variable name="blanks"
select="concat($blanks10, $blanks10, $blanks10)"/>
<!--* For Report, table1, and Detail_collection, we just
* recur on the children *-->
<xsl:template match="Report | table1 | Detail_collection">
<xsl:apply-templates select="*"/>
</xsl:template>
<!--* For Detail, we recur on the children and supply a
* line-ending newline. *-->
<xsl:template match="Detail">
<xsl:apply-templates select="*"/>
<xsl:text>
</xsl:text>
</xsl:template>
<!--* For SSN, DOB, etc., we pad the value with blanks and
* truncate at the appropriate length. *-->
<xsl:template match="SSN">
<xsl:value-of select="substring(concat(.,$blanks),1,9)"
</xsl:template>
<!--* For DOB, we assume input is yyyy/mm/dd and output should
* be yyyymmdd. *-->
<xsl:template match="DOB">
<xsl:value-of
select="substring(concat(translate(.,'/',''),$blanks),1,8)"
</xsl:template>
<xsl:template match="LastName">
<xsl:value-of select="substring(concat(.,$blanks),1,16)"
</xsl:template>
<!--* FirstName etc. left as exercise for the reader. *-->
</xsl:stylesheet>
如果Detail可以在顺序或填充上变化,则可以通过将Detail模板中对xsl:apply-templates的调用替换为此处第一个代码片段中所示的代码来规范这种变化。对于一些过程程序员来说,这种风格的代码感觉更自然;因此,我建议您在学习XSLT时有意识地避免使用它。如果您想学好XSLT,那么与xsl:apply-templates成为朋友是值得的。
如果你不关心学习XSLT,那么我的建议是希望有人回答你的问题,为你的任务提供一个完整的解决方案。