使用XSLT(复杂位置逻辑)将XML转换为定长文件

时间:2019-03-07 07:41:25

标签: xslt xslt-2.0 xslt-3.0

我们有一个要求,其中需要将XML转换为固定长度文件。 第一个记录作为标题,然后有实际记录。从2个记录开始,我们需要应用下面提到的逻辑:
1.长度为45后,考虑10个数字0000001000,         最后一位,我们需要按照以下内容进行检查和替换          表格:

      "For Positive Amount: (0000001000)   - (000000100{)             
      {= 0                                          
      A = 1                                       
      B = 2                                      
      c = 3                                      
      D = 4                                     
      E = 5                                      
      F = 6                                     
      G = 7                                      
      H = 8                                     
      I = 9 

我没有太多想法,所以创建了小型XSLT,要求任何人都可以提供帮助。

输入:

 <?xml version='1.0' encoding='utf-8'?>
<ZR>
<INPUT>
   <I_FIL>ERES</I_FIL>
  </INPUT>
  <TABLES>
   <T_ER>
    <item>
        <DATA> HEADER1111111122222222333333344456</DATA>
    </item>
    <item>
         <DATA>778944    D4E2   EA     1234567891 2018-11-060000001000EA 
       0000000000000100001020D04YA30TRE0000000XXXYYY 300{  P 2018-11-05</DATA>
    </item>
    <item>
        <DATA>987654    D4E2   EA     1987654321 2018-11-060000002001EA 
       0000000000000100001020D04YA30UUU0000000XXXLRB 100{  P 2018-11-05</DATA>
    </item>
                .
                .
                .
                .
                .
                .
                .
                .
</T_ER>
</TABLES>
</ZR>

XSLT:

       <?xml version="1.0" encoding="UTF-8"?>
   <xsl:stylesheet version="2.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   xmlns:fn="http://www.w3.org/2005/xpath-functions" >
    <xsl:output omit-xml-declaration="yes"/>
     <xsl:param name="break" select="'&#xA;'" />

       <xsl:template match="/">
      <xsl:value-of select="ZR/TABLES/T_ER/item[1]/DATA"/> 
    <xsl:value-of select="$break" />
    </xsl:template>
   </xsl:stylesheet>

预期输出:

     HEADER1111111122222222333333344456
        778944    D4E2   EA     1234567891 2018-11-06000000100{EA 
       0000000000000100001020D04YA30TRE0000000XXXYYY 300{  P 2018-11-05
    987654    D4E2   EA     1987654321 2018-11-06000000200AEA 
       0000000000000100001020D04YA30UUU0000000XXXLRB 100{  P 2018-11-05
    .
    .
    .
    .

2 个答案:

答案 0 :(得分:1)

好,使用函数string-lengthsubstringtranslate来满足您的要求,可以通过以下方式实现:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<xsl:output omit-xml-declaration="yes" />
<xsl:param name="break" select="'&#xA;'" />

 <xsl:template match="/">
    <xsl:value-of select="ZR/TABLES/T_ER/item[1]/DATA" />
    <xsl:value-of select="$break" />

    <xsl:for-each select="ZR/TABLES/T_ER/item[position() != 1]">

        <xsl:variable name="length" select="string-length(substring(DATA,0,46))" />
        <xsl:variable name="tenNumbers" select="substring(DATA, ($length + 1), 10)"/>
        <xsl:variable name="charToReplace" select="translate(substring($tenNumbers, string-length($tenNumbers), 1),'0123456789','{ABCDEFGHI')" />

        <xsl:value-of select="concat(substring(DATA,0,46), substring(DATA, ($length + 1), 9), $charToReplace, substring(DATA,($length+11),(string-length(DATA) + 1)))"/>
        <xsl:value-of select="$break" />
    </xsl:for-each>

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

答案 1 :(得分:1)

您似乎只是想根据自己的地图替换第55个字符,因此可以执行此操作...

<xsl:template match="/">
<xsl:value-of select="ZR/TABLES/T_ER/item[1]/DATA"/> 
<xsl:value-of select="$break" />
<xsl:for-each select="ZR/TABLES/T_ER/item[position() > 1]/DATA">
  <xsl:variable name="char" select="substring('{ABCDEFGHI', number(substring(., 55, 1)) + 1, 1)" />
  <xsl:value-of select="concat(substring(., 1, 54), $char, substring(., 56))" />
  <xsl:value-of select="$break" />
</xsl:for-each>
</xsl:template>

这在XSLT 1.0中有效。

XSLT 2.0解决方案可能就是这样...

<xsl:template match="/">
<xsl:value-of select="ZR/TABLES/T_ER/item[1]/DATA,
                      ZR/TABLES/T_ER/item[position() > 1]/DATA/concat(substring(., 1, 54), substring('{ABCDEFGHI', number(substring(., 55, 1)) + 1, 1), substring(., 56))" 
              separator="&#xA;" />
</xsl:template>

在XSLT 3.0中,如果您想考虑使用两个或更多字符,则可以利用map的优点是易于扩展:

<xsl:stylesheet version="3.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:map="http://www.w3.org/2005/xpath-functions/map"
    xmlns:fn="http://www.w3.org/2005/xpath-functions" >

  <xsl:output omit-xml-declaration="yes"/>
  <xsl:param name="break" select="'&#xA;'" />

  <xsl:variable name="chars" as="map(xs:string, xs:string)">
    <xsl:map>
      <xsl:map-entry key="'0'" select="'{'"/>
      <xsl:map-entry key="'1'" select="'A'"/>
      <xsl:map-entry key="'2'" select="'B'"/>
      <xsl:map-entry key="'3'" select="'C'"/>
      <xsl:map-entry key="'4'" select="'D'"/>
      <xsl:map-entry key="'5'" select="'E'"/>
      <xsl:map-entry key="'6'" select="'F'"/>
      <xsl:map-entry key="'7'" select="'G'"/>
      <xsl:map-entry key="'8'" select="'H'"/>
      <xsl:map-entry key="'9'" select="'I'"/>
    </xsl:map>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:value-of select="ZR/TABLES/T_ER/item[1]/DATA,
                          ZR/TABLES/T_ER/item[position() > 1]/DATA/concat(substring(., 1, 54), $chars(substring(., 55, 1)), substring(., 56))" separator="&#xA;" />
  </xsl:template>
</xsl:stylesheet>

XSLT 3.0中可能存在更好的方法,因此希望Martin Honnen很快会说...。