【问题标题】:XML to Fixed width text file formatted detailsXML 到固定宽度文本文件格式的详细信息
【发布时间】:2014-12-31 04:57:10
【问题描述】:

我有一个这样的 XML

<?xml version="1.0" encoding="UTF-8"?>
<Report>
  <table1>
    <Detail_Collection>
      <Detail>
        <ReceiptNo>RN12345678</ReceiptNo>
        <ReceiptDate>1980/11/11</ReceiptDate>
        <LastName>Dela Cruz</LastName>
        <FirstName>Juan</FirstName>
        <PurchaseDetails>
          <Item>Wood</Item>
          <Price>25.65</Price>
          <Quantity>2</Quantity>
        </PurchaseDetails>
        <PurchaseDetails>
          <Item>Axe</Item>
          <Price>50.56</Price>
          <Quantity>5</Quantity>
        </PurchaseDetails>
      </Detail>
    </Detail_Collection>
  </table1>
</Report>

我需要使用 XSLT 1.0 将其转换为纯文本文件

我找到了这个不错的解决方案

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

但问题是我必须根据数据类型对每个细节进行格式化,如下所示

字母数字 - 应该是 30 个字符,右侧填充空格

数字(无符号) - 应保留 15 个字符并用零填充 例如000000000012345

数字(有符号) - 应保留 15 个字符并填充零和 如果负数应表示“N”,例如N00000000012345

我的 XML 文件的输出应该是:

RN12345678                   19801111Dela Cruz                    Juan               Wood               000000000002565000000000000002
RN12345678                   19801111Dela Cruz                    Juan               Axe                000000000005056000000000000005

例如价格是负数

RN12345678                   19801111Dela Cruz                    Juan               Wood               N00000000002565000000000000002
RN12345678                   19801111Dela Cruz                    Juan               Axe                N00000000005056000000000000005

顺便说一句,我有一些字段有 300 个字符(如填充符),所以我不知道是否需要在变量 some_spaces 中放置 300 多个空格

日期应为 8 个字符 YYYYMMDD。

我有一个我正在使用的模板,但不确定如何将“N”用于负数以及如何根据要求格式化日期。

这是模板:

<xsl:template name="prepend-pad">
    <!-- recursive template to right justify and prepend the value with whatever padChar is passed in   -->
    <xsl:param name="padChar" />
    <xsl:param name="padVar" />
    <xsl:param name="length" />
    <xsl:choose>
      <xsl:when test="string-length($padVar) &lt; $length">
        <xsl:call-template name="prepend-pad">
          <xsl:with-param name="padChar" select="$padChar"/>
          <xsl:with-param name="padVar" select="concat($padChar,$padVar)"/>
          <xsl:with-param name="length" select="$length"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="substring($padVar,string-length($padVar) - $length + 1)" />
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

谢谢

【问题讨论】:

  • XSL 版本在这里可能很重要,例如访问castable as
  • 你能发布你的例子的预期结果吗? XSLT 与您的 XML 不匹配,因此不清楚您要输出哪些详细信息。 -- 另外,您没有为日期指定格式。
  • 嗨,Michael,编辑了我的帖子以包含预期的结果,谢谢

标签: xml xslt xslt-1.0


【解决方案1】:

怎么样:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="utf-8"/>

<xsl:variable name="spaces" select="'                              '"/>

<xsl:template match="/">
    <xsl:for-each select="Report/table1/Detail_Collection/Detail/PurchaseDetails">
        <xsl:apply-templates select="../ReceiptNo"/>
        <xsl:apply-templates select="../ReceiptDate"/>
        <xsl:apply-templates select="../LastName"/>
        <xsl:apply-templates select="../FirstName"/>
        <xsl:apply-templates select="Item"/>
        <xsl:call-template name="format-number">
            <xsl:with-param name="number" select="100 * Price"/>
        </xsl:call-template>
        <xsl:call-template name="format-number">
            <xsl:with-param name="number" select="Quantity"/>
        </xsl:call-template>
        <xsl:if test="position()!=last()">
            <xsl:text>&#10;</xsl:text>
        </xsl:if>
    </xsl:for-each>     
</xsl:template>

<xsl:template match="ReceiptNo | LastName | FirstName | Item">
    <xsl:value-of select="substring(concat(., $spaces), 1, 30)"/>
</xsl:template>

<xsl:template match="ReceiptDate">
    <xsl:value-of select="translate(., '/', '')"/>
</xsl:template>

<xsl:template name="format-number">
    <xsl:param name="number" select="0"/>
    <xsl:choose>
        <xsl:when test="$number >= 0">
            <xsl:value-of select="format-number($number, '000000000000000')"/>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="format-number(-$number, 'N00000000000000')"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

</xsl:stylesheet>

这部分恐怕我没看懂:

顺便说一下,我有一些有 300 个字符的字段(比如 填充)所以我不知道我是否需要在变量中放置 300+ 个空格 一些空间


编辑:

要在结果行中插入 300 个空格,我建议您简单地使用:

<xsl:text>  (300 spaces here)  </xsl:text>

可以使用命名模板动态生成任意数量的空格,但由于您需要一个常数,我看不出它有什么好处。

关于它将以 MM/dd/yyyy 形式提供给我的日期,我需要 将其格式化为 yyyyMMdd,对于我提供的错误示例数据,我们深表歉意。

如果是这样,将匹配日期字段的模板更改为:

<xsl:template match="ReceiptDate">
    <xsl:value-of select="concat(substring(., 7, 4), substring(., 1, 2), substring(., 4, 2))"/>
</xsl:template>

【讨论】:

  • 嗨迈克尔,我的意思是我的文件有一个 300 个字符的字段,应该用空格填充,它就像一个保留字段,它位于行尾之前的某个位置。并且关于它将以 MM/dd/yyyy 形式提供给我的日期,我需要将其格式化为 yyyyMMdd,对于我提供的错误示例数据感到抱歉。
  • 对不起,我还是没跟上。你的意思是你需要在输出的每一行添加300个空格(不管输入)? ---顺便说一句,您使用的是哪个处理器?如果您的处理器支持,您可能会在此处获得一些扩展功能的帮助。
  • 是的,类似的,它就像一个保留字段,我只是使用VS2013。
【解决方案2】:

如 cmets 中所述,您的 XSL 与您提供的 XML 几乎没有关系。 因此,根据对平面文本文件的模糊要求并使用填充来格式化数据,我做了一些猜测并得出以下结论。

<?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:for-each select="PurchaseDetails">
            <xsl:call-template name="format">
                <xsl:with-param name="textnode" select="../LastName"/>
                <xsl:with-param name="width" select="number(33-18)"/>
                <xsl:with-param name="type" select="string('A')"/>
            </xsl:call-template>
            <xsl:text>,</xsl:text>
            <xsl:call-template name="format">
                <xsl:with-param name="textnode" select="../FirstName"/>
                <xsl:with-param name="width" select="number(46-34)"/>
                <xsl:with-param name="type" select="string('A')"/>
            </xsl:call-template>
            <xsl:text>,</xsl:text>
            <xsl:call-template name="format">
                <xsl:with-param name="textnode" select="../ReceiptDate"/>
                <xsl:with-param name="width" select="number(54-45)"/>
                <xsl:with-param name="type" select="string('A')"/>
            </xsl:call-template>
            <xsl:text>,</xsl:text>
            <xsl:call-template name="format">
                <xsl:with-param name="textnode" select="Item"/>
                <xsl:with-param name="width" select="number(54-45)"/>
                <xsl:with-param name="type" select="string('A')"/>
            </xsl:call-template>
            <xsl:text>,</xsl:text>
            <xsl:call-template name="format">
                <xsl:with-param name="textnode" select="Quantity"/>
                <xsl:with-param name="width" select="number(54-45)"/>
                <xsl:with-param name="type" select="string('N')"/>
            </xsl:call-template>
            <xsl:call-template name="format">
                <xsl:with-param name="textnode" select="Price"/>
                <xsl:with-param name="width" select="number(54-45)"/>
                <xsl:with-param name="type" select="string('N')"/>
            </xsl:call-template>
            <xsl:text>&#10;</xsl:text>
        </xsl:for-each>
    </xsl:template>

    <xsl:template name="format">
        <xsl:param name="width"/>
        <xsl:param name="textnode"/>
        <xsl:param name="type"/>
        <xsl:variable name="leader_padding">
            <xsl:choose>
                <xsl:when test="$type='N' or $type='S'">
                    <xsl:value-of select="translate($some_spaces,' ','0')"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="$some_spaces"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:variable>

        <xsl:variable name="bigstring" select="concat($leader_padding,$textnode)"/>
        <xsl:variable name="truncatedstring" select="substring($bigstring,string-length($bigstring)-$width)"/>

        <xsl:choose>
            <xsl:when test="$type='A'">
                <xsl:text> </xsl:text>
            </xsl:when>
            <xsl:when test="$type='N' or ($type='S' and number($textnode >= 0))">
                <xsl:text>0</xsl:text>
            </xsl:when>
            <xsl:otherwise>
                <!-- type must be S and value must be negative -->
                <xsl:text>N</xsl:text>
            </xsl:otherwise>
        </xsl:choose>
        <xsl:value-of select="$truncatedstring"/>

    </xsl:template>
</xsl:stylesheet>

当针对您的输入 xml 文件运行时会产生:

    Dela Cruz,          Juan, 1980/11/11,       Wood,0000000000200000025.65
    Dela Cruz,          Juan, 1980/11/11,        Axe,0000000000500000050.56

这里有两行,因为输入格式不是很平坦。所以我选择为每件购买的商品使用一条线。

【讨论】:

    猜你喜欢
    • 2013-05-24
    • 2022-01-23
    • 2010-10-13
    • 1970-01-01
    • 2022-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多