使用xsl样式表的XML到固定宽度的文本文件

时间:2013-05-29 14:12:33

标签: xml xslt xslt-1.0 fixed-width

我需要帮助使用xsl样式表将此xml格式化为固定宽度的文本文件。我对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

非常感谢所有帮助! 提前谢谢!

3 个答案:

答案 0 :(得分:6)

在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>&#10;</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

答案 1 :(得分:6)

以下是(在我看来)lite更可靠和可维护的版本:

<?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>&#10;</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>

即使输入中的字段与请求的输出不一致,或者输入中缺少字段,它也会创建正确的输出。 此外,它还认为有多个详细信息条目。

答案 2 :(得分:2)

要在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>&#xA;</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>&#xA;</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>

如果详细信息的顺序或数量可能不同,则可以通过将详细信息模板中的xsl:apply-templates替换为此处的第一个代码片段中显示的代码来规范化变体。对于一些程序程序员来说,这种代码风格也更自然;因此,我建议你在学习XSLT时有意识地避免它。如果你想学好XSLT,与xsl:apply-templates成为朋友是值得的。

如果您不关心学习XSLT,那么我的建议是希望有人通过为您的任务提供完整的解决方案来回答您的问题。