XML 使用 XSLT(复杂位置逻辑)到固定长度文件

XML to Fixed Length File Using XSLT (Complex Position Logic)

我们有一个需求需要将XML转换成定长文件。 第一条记录为 header,之后我们有实际记录。从第 2 条记录开始,我们需要应用下面提到的逻辑:
1.After 长度 45,考虑 10 个数字 0000001000,无论是什么 我们需要按照以下步骤检查和替换最后一位数字 table:

      "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
    .
    .
    .
    .

好吧,根据您的规范使用函数 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>

看起来你只是想根据你的地图替换第 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 很快就会说....