6

我需要帮助使用 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

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

4

3 回答 3

8

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

<?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>

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

于 2013-05-29T15:40:27.563 回答
6

在 XSLT 1.0 中执行此操作的秘诀是认识到您可以将“填充策略”与“子字符串策略”结合起来,以将一段文本填充或剪切到所需的宽度。特别是这种形式的 XSLT 指令:

substring(concat('value to pad or cut', '       '), 1, 5)

... whereconcat用于向字符串添加多个填充字符并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
于 2013-05-29T14:50:28.540 回答
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>

如果 Detail 可以按顺序或总体变化,则可以通过将 Detail 模板中对 xsl:apply-templates 的调用替换为此处第一个代码片段中所示的代码来规范化变化。这种代码风格对一些程序程序员来说也更自然。因此,我建议您在学习 XSLT 时有意识地避免它。如果您想学好 XSLT,与 xsl:apply-templates 成为朋友是值得的。

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

于 2013-05-29T15:46:45.010 回答