SAP ABAP端实现解析具有深层结构的文件内容(类似SAP PO中的FCC功能)

前言

日本项目经常需要通过PI或者CPI去解析一些文件内容,如果只是扁平结构的,不涉及头行结构的话,在PI中可以FCC(File Content Conversion)进行解析转换,在CPI中可以使用groovy脚本进行解析转换,但是如果涉及头行层级关系的,PI中不支持通过FCC进行转换,但是可以通过一些自定义的转换Bean来进行转换,不过需要自己去编写java代码去实现,并导出为EAR文件部署到PI服务器上才可以,当时没有现成的java解析代码,因为一些因素最终选择在SAP端使用ABAP来实现这个解析功能,不过理论上我觉得应该采用java的方式在PI端实现自定义转换bean来完成解析动作,因为PI端有更完善的监控机制。

需求

源文件内容如下:

A012A010123456789
A0200010ABCDEFGHI12345678
A012A010123456789
A0200010ABCDEFGHI12345678
A0200020ABCDEFGHI12345678

转换规则:

第2位~第3位为01的为抬头数据,第2位~第3位为02的为明细数据。

预期目标XML格式:

<root>
	<head>
		<type>A</type>
		<subtype>01</subtype>
		<company>2A01</company>
		<description>0123456789</description>
		<items>
			<item>
				<type>A</type>
				<subtype>02</subtype>
				<itemno>00010</itemno>
				<text1>ABCDEFGHI</text1>
				<text2>12345678</text2>
			</item>
		</items>
	</head>
	<head>
		<type>A</type>
		<subtype>01</subtype>
		<company>2A01</company>
		<description>0123456789</description>
		<items>
			<item>
				<type>A</type>
				<subtype>02</subtype>
				<itemno>00010</itemno>
				<text1>ABCDEFGHI</text1>
				<text2>12345678</text2>
			</item>
			<item>
				<type>A</type>
				<subtype>02</subtype>
				<itemno>00020</itemno>
				<text1>ABCDEFGHI</text1>
				<text2>12345678</text2>
			</item>
		</items>
	</head>
</root>

但实际上PI并不支持这种层级转换,通过FCC配置只能达到平级的效果,配置如下:

转换结果:

<?xml version="1.0" encoding="utf-8"?>
<ns:MT_FTP2SAP_HD xmlns:ns="urn:psam:ftp2sap_test_head_item">
	<head>
		<type>A</type>
		<subtype>01</subtype>
		<company>2A01</company>
		<description>0123456789</description>
	</head>
	<item>
		<type>A</type>
		<subtype>02</subtype>
		<itemno>00010</itemno>
		<text1>ABCDEFGHI</text1>
		<text2>12345678</text2>
	</item>
	<head>
		<type>A</type>
		<subtype>01</subtype>
		<company>2A01</company>
		<description>0123456789</description>
	</head>
	<item>
		<type>A</type>
		<subtype>02</subtype>
		<itemno>00010</itemno>
		<text1>ABCDEFGHI</text1>
		<text2>12345678</text2>
	</item>
	<item>
		<type>A</type>
		<subtype>02</subtype>
		<itemno>00020</itemno>
		<text1>ABCDEFGHI</text1>
		<text2>12345678</text2>
	</item>
</ns:MT_FTP2SAP_HD>

这样平级的转换效果,就失去了层级的关联关系,由于源文件中的数据是没有主键信息可以互相关联的,只能通过每一行的位置来判定是不是上一行的明细行,所以这样的转换结果是不行的,所以后来采用将每一行字符串完整的接收到SAP,在SAP端进行解析,考虑到类似的接口比较多,不想每一本单独去写解析规则,所以我在SAP端封装了一个共通的解析工具类,可以通过传入一些可配置的解析参数,来动态进行解析。

通过FCC转换文件到XML可以参考我的另一篇帖子:

SAP PI/PO File2Soap 发送方使用Content Conversion将文本格式转换为XML格式icon-default.png?t=N7T8https://blog.csdn.net/DeveloperMrMeng/article/details/130868786?spm=1001.2014.3001.5501


实现效果

1.测试数据内容:

2.解析规则参数传值:

配置参数说明:

STRUCTURE:包含哪几种类型的解析规则。

KEYFNAME:每种类型的判定依据,该字段要在每种类型的结构中都有,如果只有一种类型的数据(只有一套解析规则),则不需要指定该参数。

<Structure>.FIELDNAMES:该类型包含哪些字段,以逗号隔开。

<Structure>.FIELDLEN:每个字段的长度,以逗号隔开,框架会按照指定长度去解析。

<Structure>.FIELDSEPARATOR:指定分隔符,内容将会根据分隔符拆分,FIELDSEPARATORFIELDLEN指定指定其中一种作为解析方式。

<Structure>.FIELDLENTYPE:解析长度的类型,BYTE为按字节长度解析,STRING为按照字符长度,不指定默认为STRING。

<Structure>.PARENT:指定父节点,如果该节点为顶层节点,则指定为ROOT即可。

3.调用解析工具类:

4.解析结果:

同理,只需要根据参数指定不同的层级关系,即可达到不同的解析效果:

①:将item 和 details均指定为 head 的子节点

②:只需要解析 head 数据。

借助该解析工具类,即可实现通过配置的方式来动态解析文件内容,并且同样适用于本地文件直接通过GUI_UPLOAD的方式上传后去进行解析。

工具类源码

实现过程中要考虑的问题点还是蛮多的,要考虑通用性的问题,所以要考虑的尽可能全面一些,工具类主要使用了一些递归方法,以及创建结构、表的一些SAP标准工具类,测试demo程序源码也会在下面附上,感兴趣的可以自己创建一下,debug一下。

CLASS zprbccl_convert_file2table DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.

    TYPES:
      BEGIN OF ty_line,
        name TYPE string,
        line TYPE REF TO data,
      END OF ty_line .
    TYPES:
      tt_line   TYPE STANDARD TABLE OF ty_line .
    TYPES:
      tt_string TYPE STANDARD TABLE OF string .
    TYPES:
      BEGIN OF ty_source_data,
        data TYPE string,
      END OF ty_source_data .
    TYPES:
      tt_source_data TYPE STANDARD TABLE OF ty_source_data .
    TYPES:
      BEGIN OF ty_convert_rule,
        param TYPE string,
        value TYPE string,
      END OF ty_convert_rule .
    TYPES:
      tt_convert_rule TYPE STANDARD TABLE OF ty_convert_rule .

    CLASS-METHODS convert_file2table
      IMPORTING
        !it_source_data  TYPE tt_source_data
        !it_convert_rule TYPE tt_convert_rule
      EXPORTING
        !es_result       TYPE REF TO data
        !et_error        TYPE bapiret2_t
      EXCEPTIONS
        convert_failed .
  PROTECTED SECTION.
  PRIVATE SECTION.

    CLASS-DATA mt_struc_name TYPE zprbctstring .
    CLASS-DATA mt_line TYPE tt_line .
    CLASS-DATA mt_line_type TYPE zprbct_convert_line_type .
    CLASS-DATA mt_final_components TYPE abap_component_tab .

    CLASS-METHODS add_component
      IMPORTING
        !iv_fname        TYPE string
        !iv_data_element TYPE any
      CHANGING
        !ct_components   TYPE abap_component_tab .
    CLASS-METHODS build_component
      IMPORTING
        !it_source_data  TYPE tt_source_data
        !it_convert_rule TYPE tt_convert_rule
        !iv_strucname    TYPE string
      CHANGING
        !ct_components   TYPE abap_component_tab
        !ct_error        TYPE bapiret2_t .
    CLASS-METHODS fill_line
      IMPORTING
        !is_source_data  TYPE ty_source_data
        !it_convert_rule TYPE tt_convert_rule
      CHANGING
        !cs_struc        TYPE any
        !ct_error        TYPE bapiret2_t .
    CLASS-METHODS add_data_to_mt_line
      IMPORTING
        !iv_struc_name      TYPE string
        !it_sep_field_value TYPE tt_string .
    CLASS-METHODS update_child_to_mt_line
      IMPORTING
        !iv_struc_name      TYPE string
        !it_sep_field_value TYPE tt_string
        !it_convert_rule    TYPE tt_convert_rule .
    CLASS-METHODS update_target_child_table
      IMPORTING
        !iv_struc_name      TYPE string
        !it_sep_field_value TYPE tt_string
      CHANGING
        !ct_parent_list     TYPE tt_string
        !ct_line            TYPE any .
ENDCLASS.



CLASS ZPRBCCL_CONVERT_FILE2TABLE IMPLEMENTATION.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Private Method ZPRBCCL_CONVERT_FILE2TABLE=>ADD_COMPONENT
* +-------------------------------------------------------------------------------------------------+
* | [--->] IV_FNAME                       TYPE        STRING
* | [--->] IV_DATA_ELEMENT                TYPE        ANY
* | [<-->] CT_COMPONENTS                  TYPE        ABAP_COMPONENT_TAB
* +--------------------------------------------------------------------------------------</SIGNATURE>
  METHOD add_component.
    DATA:
      lo_type       TYPE REF TO cl_abap_typedescr,
      ls_compoments TYPE abap_componentdescr.

    ls_compoments-name = iv_fname.

    CALL METHOD cl_abap_datadescr=>describe_by_name
      EXPORTING
        p_name         = iv_data_element
      RECEIVING
        p_descr_ref    = lo_type
      EXCEPTIONS
        type_not_found = 1
        OTHERS         = 2.

    ls_compoments-type ?= lo_type.
    APPEND ls_compoments TO ct_components.
  ENDMETHOD.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Private Method ZPRBCCL_CONVERT_FILE2TABLE=>ADD_DATA_TO_MT_LINE
* +-------------------------------------------------------------------------------------------------+
* | [--->] IV_STRUC_NAME                  TYPE        STRING
* | [--->] IT_SEP_FIELD_VALUE             TYPE        TT_STRING
* +--------------------------------------------------------------------------------------</SIGNATURE>
  METHOD add_data_to_mt_line.
    DATA:
      ls_line  TYPE ty_line,
      lo_struc TYPE REF TO data.

    READ TABLE mt_line_type INTO DATA(ls_line_type)
      WITH KEY name = iv_struc_name.

    CREATE DATA lo_struc TYPE HANDLE ls_line_type-type.
    ASSIGN lo_struc->* TO FIELD-SYMBOL(<fs_new_struc>).

    LOOP AT it_sep_field_value INTO DATA(ls_field_value).
      ASSIGN COMPONENT sy-tabix OF STRUCTURE <fs_new_struc> TO FIELD-SYMBOL(<fs_field_value>).
      IF <fs_field_value> IS ASSIGNED.
        <fs_field_value> = ls_field_value.
      ENDIF.
      UNASSIGN <fs_field_value>.
    ENDLOOP.

    ls_line-name = iv_struc_name.
    ls_line-line = lo_struc.
    APPEND ls_line TO mt_line.
    CLEAR ls_line.
  ENDMETHOD.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Private Method ZPRBCCL_CONVERT_FILE2TABLE=>BUILD_COMPONENT
* +-------------------------------------------------------------------------------------------------+
* | [--->] IT_SOURCE_DATA                 TYPE        TT_SOURCE_DATA
* | [--->] IT_CONVERT_RULE                TYPE        TT_CONVERT_RULE
* | [--->] IV_STRUCNAME                   TYPE        STRING
* | [<-->] CT_COMPONENTS                  TYPE        ABAP_COMPONENT_TAB
* | [<-->] CT_ERROR                       TYPE        BAPIRET2_T
* +--------------------------------------------------------------------------------------</SIGNATURE>
  METHOD build_component.
    DATA:
      ls_error TYPE bapiret2.

    DATA:
      lt_struc_name TYPE STANDARD TABLE OF string,
      lt_fnames     TYPE STANDARD TABLE OF string,
      lt_flen       TYPE STANDARD TABLE OF string,
      lv_param      TYPE string.

    DATA:
      lt_components TYPE abap_component_tab,
      ls_compoments TYPE abap_componentdescr,
      lo_strucdescr TYPE REF TO cl_abap_structdescr,
      lo_tabledescr TYPE REF TO cl_abap_tabledescr,
      lo_struc      TYPE REF TO data,
      lo_table      TYPE REF TO data.

    DATA:
      ls_line_type TYPE zprbcs_convert_line_type.

    DATA:
      lo_excep_table_creation  TYPE REF TO cx_sy_table_creation,
      lo_excep_struct_creation TYPE REF TO cx_sy_struct_creation.

**********************************************************************
*   Check Paramters
**********************************************************************
*   <Structure>.FIELDNAMES
    lv_param = iv_strucname && '.FIELDNAMES'.

    READ TABLE it_convert_rule INTO DATA(ls_conver_fnames)
      WITH KEY param = lv_param.
    IF sy-subrc <> 0.
      ls_error-type    = 'E'.
      ls_error-message = 'Parameter "<Structure>.FIELDNAMES" is requeried.'(002).
      APPEND ls_error TO ct_error.
      RETURN.
    ELSE.
      SPLIT ls_conver_fnames-value AT ',' INTO TABLE lt_fnames.

*     <Structure>.FIELDSEPARATOR
      lv_param = iv_strucname && '.FIELDSEPARATOR'.

      READ TABLE it_convert_rule INTO DATA(ls_conver_fsep)
        WITH KEY param = lv_param.

*     <Structure>.FIELDLEN
      lv_param = iv_strucname && '.FIELDLEN'.

      READ TABLE it_convert_rule INTO DATA(ls_conver_flen)
        WITH KEY param = lv_param.

      IF ls_conver_fsep IS INITIAL AND ls_conver_flen IS INITIAL.
        ls_error-type    = 'E'.
        ls_error-message = 'Parameter "<Structure>.FIELDLEN" and "<Structure>.FIELDSEPARATOR" require at least one.'(003).
        APPEND ls_error TO ct_error.
        RETURN.
      ENDIF.

      IF ls_conver_flen IS NOT INITIAL.
        SPLIT ls_conver_flen-value AT ',' INTO TABLE lt_flen.

        IF lines( lt_fnames ) <> lines( lt_flen ).
          ls_error-type    = 'E'.
          ls_error-message = '"<Structure>.FIELDNAMES" and "<Structure>.FIELDLEN" do not match.'(004).
          APPEND ls_error TO ct_error.
          RETURN.
        ENDIF.
      ENDIF.
    ENDIF.

    CHECK ct_error IS INITIAL.

**********************************************************************
*   Build Structure
**********************************************************************
    LOOP AT lt_fnames INTO DATA(ls_fnames).
*     Add Field
      add_component(
        EXPORTING
          iv_fname        = ls_fnames
          iv_data_element = 'STRING'
        CHANGING
          ct_components   = ct_components
      ).
    ENDLOOP.

    LOOP AT it_convert_rule INTO DATA(ls_convert_parent)
      WHERE value = iv_strucname
        AND param CS '.PARENT'.

      lv_param = shift_right( val = ls_convert_parent-param
                              sub = '.PARENT' ).
*     Build Child Structure
      build_component(
        EXPORTING
          it_source_data  = it_source_data
          it_convert_rule = it_convert_rule
          iv_strucname    = lv_param
        CHANGING
          ct_components   = lt_components
          ct_error        = ct_error
       ).

      IF ct_error IS INITIAL.

        DELETE mt_struc_name WHERE table_line = lv_param.

        TRY.
*           Get line type
            CALL METHOD cl_abap_structdescr=>create
              EXPORTING
                p_components = lt_components
              RECEIVING
                p_result     = lo_strucdescr.

            ls_line_type-name = lv_param.
            ls_line_type-type = lo_strucdescr.
            APPEND ls_line_type TO mt_line_type.
            CLEAR ls_line_type.

            TRY.
*               Get table type
                CALL METHOD cl_abap_tabledescr=>create
                  EXPORTING
                    p_line_type = lo_strucdescr
                  RECEIVING
                    p_result    = lo_tabledescr.

*               Add Child field as Table Type
                ls_compoments-name =  lv_param.
                ls_compoments-type ?= lo_tabledescr.
                APPEND ls_compoments TO ct_components.
                CLEAR:
                  ls_compoments,
                  lt_components.

              CATCH cx_sy_table_creation INTO lo_excep_table_creation.
                ls_error-type    = 'E'.
                ls_error-message = lo_excep_table_creation->get_text( ).
                APPEND ls_error TO ct_error.
                RETURN.
            ENDTRY.
          CATCH cx_sy_struct_creation INTO lo_excep_struct_creation.
            ls_error-type    = 'E'.
            ls_error-message = lo_excep_struct_creation->get_text( ).
            APPEND ls_error TO ct_error.
            RETURN.
        ENDTRY.
      ENDIF.
    ENDLOOP.
  ENDMETHOD.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Public Method ZPRBCCL_CONVERT_FILE2TABLE=>CONVERT_FILE2TABLE
* +-------------------------------------------------------------------------------------------------+
* | [--->] IT_SOURCE_DATA                 TYPE        TT_SOURCE_DATA
* | [--->] IT_CONVERT_RULE                TYPE        TT_CONVERT_RULE
* | [<---] ES_RESULT                      TYPE REF TO DATA
* | [<---] ET_ERROR                       TYPE        BAPIRET2_T
* | [EXC!] CONVERT_FAILED
* +--------------------------------------------------------------------------------------</SIGNATURE>
  METHOD convert_file2table.
**********************************************************************
* This method is used to parse files into the internal table.
*   # 1.you can convert simple file to internal table.
*   # 2.you can convert deep structure file to deep internal table.
*   # 3.you can split line data by symbol or fix length(BYTE/STRING).
*   # 4.if the data has diffrent type,KEYNAME and KEYVALUE attribute must defined.
*
* Author:HAND15
* Date:  20230823
*
* Example:
*  *------------------------------------------------------------------
*  | ①Simple file source data:
*  *------------------------------------------------------------------
*     A012A011234567890
*     A012A029876543210
*  *------------------------------------------------------------------
*  |  Convert rule
*  *------------------------------------------------------------------
*    [PARAM]           |     [VALUE]
*     STRUCTURE               HEAD
*     HEAD.FIELDNAMES         TYPE,KEY,BUKRS,VBELN
*     HEAD.FIELDLEN           1,2,4,10
*     HEAD.FIELDLENTYPE       BYTE
*  *-----------------------------------------------------------------
*  |  Result table
*  *-----------------------------------------------------------------
*     HEAD
*       ∟TYPE|KEY|BUKRS|VBELN
*         A    01  2A01  1234567890
*         A    01  2A01  9876543210
*
*  *------------------------------------------------------------------
*  | ②Complex deep file source data:
*  *------------------------------------------------------------------
*     A012A01
*     A0200010ABCDEF
*     A03TEXT
*     A012A01
*     A0200010HIGKLM
*     A03TEXT1
*     A03TEXT2
*     A0200020NOPQRS
*  *------------------------------------------------------------------
*  |  Convert rule
*  *------------------------------------------------------------------
*    [PARAM]           |     [VALUE]
*     STRUCTURE               HEAD,ITEMS,DETAILS
*     KEYFNAME                KEY
*     HEAD.FIELDNAMES         TYPE,KEY,BUKRS
*     HEAD.FIELDLEN           1,2,4
*     HEAD.FIELDLENTYPE       BYTE
*     HEAD.KEYVALUE           01
*     HEAD.PARENT             ROOT
*     ITEMS.FIELDNAMES        TYPE,KEY,POSNR,MATNR
*     ITEMS.FIELDLEN          1,2,5,6
*     ITEMS.FIELDLENTYPE      BYTE
*     ITEMS.KEYVALUE          02
*     ITEMS.PARENT            HEAD
*     DETAILS.FIELDNAMES      TYPE,KEY,DESCRIP
*     DETAILS.FIELDLEN        1,2,20
*     DETAILS.FIELDLENTYPE    BYTE
*     DETAILS.KEYVALUE        03
*     DETAILS.PARENT          ITEMS
*  *-----------------------------------------------------------------
*  |  Result table
*  *-----------------------------------------------------------------
*     HEAD
*       ∟TYPE|KEY|BUKRS|ITEMS
*       | A    01  2A01   |
*       |                 ∟TYPE|KEY|POSNR|MATNR|DETAILS
*       |                   A    02  00010 ABCDEF   |
*       |                                           ∟TYPE|KEY|DESCRIP
*       |                                             A    03  TEXT
*       ∟TYPE|KEY|BUKRS|ITEMS
*         A    01  2A01   |
*                         ∟TYPE|KEY|POSNR|MATNR|DETAILS
*                         | A    02  00010 HIGKLM   |
*                         |                         ∟TYPE|KEY|DESCRIP
*                         |                         | A    03  TEXT1
*                         |                         ∟TYPE|KEY|DESCRIP
*                         |                           A    03  TEXT2
*                         ∟TYPE|KEY|POSNR|MATNR|DETAILS
*                           A    02  00010 NOPQRS
**********************************************************************
    DATA:
      lt_struc_name TYPE STANDARD TABLE OF string,
      lt_fnames     TYPE STANDARD TABLE OF string,
      lt_flen       TYPE STANDARD TABLE OF string,
      lv_param      TYPE string.

    DATA:
      ls_error  TYPE bapiret2,
      lo_result TYPE REF TO data.

    DATA:
      lt_components TYPE abap_component_tab,
      lo_strucdescr TYPE REF TO cl_abap_structdescr,
      lo_tabledescr TYPE REF TO cl_abap_tabledescr,
      lo_struc      TYPE REF TO data,
      lo_table      TYPE REF TO data.

    DATA:
      ls_line_type TYPE zprbcs_convert_line_type.

    DATA:
      ls_compoments       TYPE abap_componentdescr.

    FIELD-SYMBOLS:
      <fs_table> TYPE STANDARD TABLE.

*   <Structure>
    lv_param = 'STRUCTURE'.

    READ TABLE it_convert_rule INTO DATA(ls_convert_struc)
      WITH KEY param = lv_param.

    IF sy-subrc <> 0.
      ls_error-type    = 'E'.
      ls_error-message = 'Parameter "STRUCTURE" is requeried.'(001).
      APPEND ls_error TO et_error.
      RAISE convert_failed.
    ELSE.
      SPLIT ls_convert_struc-value AT ',' INTO TABLE mt_struc_name.

      LOOP AT mt_struc_name INTO DATA(ls_struc_name).
*       Recursively generated structure
        build_component(
          EXPORTING
            it_source_data  = it_source_data
            it_convert_rule = it_convert_rule
            iv_strucname    = ls_struc_name
          CHANGING
            ct_components   = lt_components
            ct_error        = et_error
         ).

        TRY.
*           Get line type
            CALL METHOD cl_abap_structdescr=>create
              EXPORTING
                p_components = lt_components
              RECEIVING
                p_result     = lo_strucdescr.

            ls_line_type-name = ls_struc_name.
            ls_line_type-type = lo_strucdescr.
            APPEND ls_line_type TO mt_line_type.
            CLEAR ls_line_type.

            TRY.
*               Get table type
                CALL METHOD cl_abap_tabledescr=>create
                  EXPORTING
                    p_line_type = lo_strucdescr
                  RECEIVING
                    p_result    = lo_tabledescr.

*               Build Final Structure
                ls_compoments-name = ls_struc_name.
                ls_compoments-type ?= lo_tabledescr.
                APPEND ls_compoments TO mt_final_components.
              CATCH cx_sy_table_creation INTO DATA(lo_excep_table_creation).
                ls_error-type    = 'E'.
                ls_error-message = lo_excep_table_creation->get_text( ).
                APPEND ls_error TO et_error.
                RAISE convert_failed.
            ENDTRY.
          CATCH cx_sy_struct_creation INTO DATA(lo_excep_struct_creation).
            ls_error-type    = 'E'.
            ls_error-message = lo_excep_struct_creation->get_text( ).
            APPEND ls_error TO et_error.
            RAISE convert_failed.
        ENDTRY.

        CLEAR:
          ls_compoments,
          lt_components.
      ENDLOOP.
    ENDIF.

*   Create Final Table and Structure
    TRY.
*       Get line type
        CALL METHOD cl_abap_structdescr=>create
          EXPORTING
            p_components = mt_final_components
          RECEIVING
            p_result     = lo_strucdescr.

        CREATE DATA lo_struc  TYPE HANDLE lo_strucdescr.
        ASSIGN lo_struc->* TO FIELD-SYMBOL(<fs_struc>).

        CLEAR mt_struc_name .
        SPLIT ls_convert_struc-value AT ',' INTO TABLE mt_struc_name.

        LOOP AT it_source_data INTO DATA(ls_source_data).
*         Mapping file data to targer structure fileds
          fill_line(
            EXPORTING
              is_source_data  = ls_source_data
              it_convert_rule = it_convert_rule
            CHANGING
              cs_struc        = <fs_struc>
              ct_error        = et_error
           ).

          IF et_error IS NOT INITIAL.
            RAISE convert_failed.
          ENDIF.
        ENDLOOP.

*       Append residual data to result table
        LOOP AT mt_line INTO DATA(ls_line).
          ASSIGN COMPONENT ls_line-name OF STRUCTURE <fs_struc> TO <fs_table>.
          ASSIGN COMPONENT 'LINE' OF STRUCTURE ls_line TO FIELD-SYMBOL(<fs_ref_line>).
          ASSIGN <fs_ref_line>->* TO FIELD-SYMBOL(<fs_line>).
          APPEND <fs_line> TO <fs_table>.

          DELETE TABLE mt_line FROM ls_line.
        ENDLOOP.

        CREATE DATA es_result TYPE HANDLE lo_strucdescr.
        ASSIGN es_result->* TO FIELD-SYMBOL(<fs_result>).
        <fs_result> = <fs_struc>.

      CATCH cx_sy_struct_creation INTO lo_excep_struct_creation.
        ls_error-type    = 'E'.
        ls_error-message = lo_excep_struct_creation->get_text( ).
        APPEND ls_error TO et_error.
        RETURN.
    ENDTRY.

  ENDMETHOD.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Private Method ZPRBCCL_CONVERT_FILE2TABLE=>FILL_LINE
* +-------------------------------------------------------------------------------------------------+
* | [--->] IS_SOURCE_DATA                 TYPE        TY_SOURCE_DATA
* | [--->] IT_CONVERT_RULE                TYPE        TT_CONVERT_RULE
* | [<-->] CS_STRUC                       TYPE        ANY
* | [<-->] CT_ERROR                       TYPE        BAPIRET2_T
* +--------------------------------------------------------------------------------------</SIGNATURE>
  METHOD fill_line.
    DATA:
      ls_error           TYPE bapiret2,
      lv_param           TYPE string,
      lt_fnames          TYPE STANDARD TABLE OF string,
      lt_flen            TYPE STANDARD TABLE OF string,
      lt_sep_field_value TYPE STANDARD TABLE OF string,
      lv_source_data     TYPE string,
      lv_field_value     TYPE string,
      lv_lengh_type      TYPE string,
      lv_strat_offset    TYPE i,
      lv_single_value    TYPE string,
      lv_single_length   TYPE i,
      lv_length_sum      TYPE i,
      lv_tabix           TYPE sy-tabix,
      lv_mapped          TYPE flag.

    DATA:
      lo_data  TYPE REF TO data,
      lo_struc TYPE REF TO data,
      ls_line  TYPE ty_line.

    FIELD-SYMBOLS:
      <fs_table> TYPE STANDARD TABLE.

**********************************************************************
*   Determine the type of the data in the row based on the key value
**********************************************************************
    LOOP AT mt_struc_name INTO DATA(ls_struc_name).

      lv_source_data = is_source_data-data.

      lv_param = 'KEYFNAME'.

      READ TABLE it_convert_rule INTO DATA(ls_convert_key)
        WITH KEY param = lv_param.

*     <Structure>.FIELDNAMES
      lv_param = ls_struc_name && '.FIELDNAMES'.

      READ TABLE it_convert_rule INTO DATA(ls_convert_fnames)
        WITH KEY param = lv_param.

      SPLIT ls_convert_fnames-value AT ',' INTO TABLE lt_fnames.

*     <Structure>.FIELDLEN
      lv_param = ls_struc_name && '.FIELDLEN'.

      READ TABLE it_convert_rule INTO DATA(ls_convert_flen)
        WITH KEY param = lv_param.

*     <Structure>.FIELDLENTYPE
      lv_param = ls_struc_name && '.FIELDLENTYPE'.

      READ TABLE it_convert_rule INTO DATA(ls_convert_flentype)
        WITH KEY param = lv_param.

*     <Structure>.FIELDSEPARATOR
      lv_param = ls_struc_name && '.FIELDSEPARATOR'.

      READ TABLE it_convert_rule INTO DATA(ls_convert_fsep)
        WITH KEY param = lv_param.

**********************************************************************
*     Parsing by fixed length
**********************************************************************
      IF ls_convert_flen IS NOT INITIAL.
        SPLIT ls_convert_flen-value AT ',' INTO TABLE lt_flen.

        LOOP AT lt_flen INTO DATA(ls_flen).
          DO.
            TRY .
                lv_single_value = lv_source_data+0(1).

                IF ls_convert_flentype-value = 'BYTE'.
                  lv_single_length = cl_abap_list_utilities=>dynamic_output_length( lv_single_value ).
                ELSE.
                  lv_single_length = strlen( lv_single_value ).
                ENDIF.

                lv_length_sum = lv_length_sum + lv_single_length.

                IF lv_length_sum > ls_flen.
                  lv_field_value = lv_field_value && ` `.
                  APPEND lv_field_value TO lt_sep_field_value.

                  CLEAR:
                    lv_field_value,
                    lv_length_sum.

                  SHIFT lv_source_data BY 1 PLACES LEFT.
                  lv_source_data = ` ` && lv_source_data.

                  EXIT.
                ENDIF.

                IF lv_length_sum = ls_flen.
                  lv_field_value = lv_field_value && lv_single_value.
                  APPEND lv_field_value TO lt_sep_field_value.

                  CLEAR:
                    lv_field_value,
                    lv_length_sum.

                  SHIFT lv_source_data BY 1 PLACES LEFT.

                  EXIT.
                ENDIF.

                IF lv_length_sum < ls_flen..
                  lv_field_value = lv_field_value && lv_single_value.
                  SHIFT lv_source_data BY 1 PLACES LEFT.
                ENDIF.
              CATCH cx_sy_range_out_of_bounds.
                APPEND lv_field_value TO lt_sep_field_value.

                CLEAR:
                    lv_field_value,
                    lv_length_sum.

                EXIT.
            ENDTRY.
          ENDDO.
        ENDLOOP.
      ENDIF.

**********************************************************************
*     Parsing by separator
**********************************************************************
      IF ls_convert_fsep IS NOT INITIAL.
        SPLIT lv_source_data AT ls_convert_fsep-value INTO TABLE lt_sep_field_value.
      ENDIF.

**********************************************************************
*     IF defined the KEYFNAME attribute
**********************************************************************
      IF ls_convert_key-value IS NOT INITIAL.
*       Get key field index
        READ TABLE lt_fnames TRANSPORTING NO FIELDS
          WITH KEY table_line = ls_convert_key-value.

*       Get key field index value
        READ TABLE lt_sep_field_value INTO lv_field_value INDEX sy-tabix.

*       <Structure>.KEYVALUE
        lv_param = ls_struc_name && '.KEYVALUE'.

*       Get defined key value
        READ TABLE it_convert_rule INTO DATA(ls_convert_keyvalue)
          WITH KEY param = lv_param.

*       Check key value is same
        IF lv_field_value = ls_convert_keyvalue-value.
          lv_mapped = abap_true.

          READ TABLE mt_line INTO ls_line
            WITH KEY name = ls_struc_name.
*         Means here is new line and has a old line
          IF sy-subrc = 0.
            ASSIGN COMPONENT ls_struc_name OF STRUCTURE cs_struc TO <fs_table>.
            ASSIGN COMPONENT 'LINE' OF STRUCTURE ls_line TO FIELD-SYMBOL(<fs_ref_line>).
            ASSIGN <fs_ref_line>->* TO FIELD-SYMBOL(<fs_line>).
            APPEND <fs_line> TO <fs_table>.

            DELETE mt_line WHERE name = ls_struc_name.

*           Add line to global memory
            add_data_to_mt_line(
              EXPORTING
                iv_struc_name      = ls_struc_name
                it_sep_field_value = lt_sep_field_value
             ).
          ELSE.
            READ TABLE mt_final_components TRANSPORTING NO FIELDS
              WITH KEY name = ls_struc_name.
*           Means here is new line and it's first line
            IF sy-subrc = 0.

*             Add line to global memory
              add_data_to_mt_line(
                EXPORTING
                  iv_struc_name      = ls_struc_name
                  it_sep_field_value = lt_sep_field_value
               ).

*           Means here is a child line
            ELSE.

*             Update the fields of the corresponding node
              update_child_to_mt_line(
                EXPORTING
                  iv_struc_name      = ls_struc_name
                  it_sep_field_value = lt_sep_field_value
                  it_convert_rule    = it_convert_rule
               ).
            ENDIF.
          ENDIF.
        ELSE.
          CLEAR:
            lt_sep_field_value,
            lv_field_value.
        ENDIF.

**********************************************************************
*     If not defined KEY attribute
**********************************************************************
      ELSE.
        lv_mapped = abap_true.

        ASSIGN COMPONENT 1 OF STRUCTURE cs_struc TO <fs_table>.

        READ TABLE mt_line_type INTO DATA(ls_line_type)
          WITH KEY name = ls_struc_name.

        CREATE DATA lo_struc TYPE HANDLE ls_line_type-type.
        ASSIGN lo_struc->* TO FIELD-SYMBOL(<fs_new_struc>).

        LOOP AT lt_sep_field_value INTO DATA(ls_field_value).
          ASSIGN COMPONENT sy-tabix OF STRUCTURE <fs_new_struc> TO FIELD-SYMBOL(<fs_field_value>).
          IF <fs_field_value> IS ASSIGNED.
            <fs_field_value> = ls_field_value.
          ENDIF.

          UNASSIGN <fs_field_value>.
        ENDLOOP.

        APPEND <fs_new_struc> TO <fs_table>.
      ENDIF.

      IF lv_mapped = abap_true.
        RETURN.
      ENDIF.
    ENDLOOP.
  ENDMETHOD.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Private Method ZPRBCCL_CONVERT_FILE2TABLE=>UPDATE_CHILD_TO_MT_LINE
* +-------------------------------------------------------------------------------------------------+
* | [--->] IV_STRUC_NAME                  TYPE        STRING
* | [--->] IT_SEP_FIELD_VALUE             TYPE        TT_STRING
* | [--->] IT_CONVERT_RULE                TYPE        TT_CONVERT_RULE
* +--------------------------------------------------------------------------------------</SIGNATURE>
  METHOD update_child_to_mt_line.
    DATA:
      lv_param       TYPE string,
      lv_struc_name  TYPE string,
      lv_start_index TYPE sy-index,
      lt_parent_list TYPE STANDARD TABLE OF string.

    DATA:
      lo_struc     TYPE REF TO data,
      lt_table_ref TYPE REF TO data.

    FIELD-SYMBOLS:
      <fs_result_table> TYPE STANDARD TABLE.

    lv_struc_name = iv_struc_name.

    APPEND lv_struc_name TO lt_parent_list.

**********************************************************************
*   Find parents list
**********************************************************************
    DO.
      lv_param = lv_struc_name && '.PARENT'.

      READ TABLE it_convert_rule INTO DATA(ls_convert_parent)
        WITH KEY param = lv_param.
      IF sy-subrc = 0.
        IF ls_convert_parent-value <> 'ROOT'.
          INSERT ls_convert_parent-value INTO lt_parent_list INDEX 1.
        ENDIF.

        READ TABLE mt_final_components TRANSPORTING NO FIELDS
          WITH KEY name = ls_convert_parent-value.
        IF sy-subrc <> 0.
          lv_struc_name = ls_convert_parent-value.
        ELSE.
          EXIT.
        ENDIF.
      ELSE.
        EXIT.
      ENDIF.
    ENDDO.

    READ TABLE lt_parent_list INTO DATA(ls_parent_top) INDEX 1.

    READ TABLE mt_line INTO DATA(ls_line)
      WITH KEY name = ls_parent_top.

    ASSIGN COMPONENT 'LINE' OF STRUCTURE ls_line TO FIELD-SYMBOL(<fs_ref_line>).
    ASSIGN <fs_ref_line>->* TO FIELD-SYMBOL(<fs_line>).

    IF iv_struc_name <> ls_parent_top.
      DELETE lt_parent_list INDEX 1.

*     Update child note fields value
      update_target_child_table(
        EXPORTING
          iv_struc_name      = iv_struc_name
          it_sep_field_value = it_sep_field_value
        CHANGING
          ct_line            = <fs_line>
          ct_parent_list     = lt_parent_list
      ).
    ENDIF.

  ENDMETHOD.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Private Method ZPRBCCL_CONVERT_FILE2TABLE=>UPDATE_TARGET_CHILD_TABLE
* +-------------------------------------------------------------------------------------------------+
* | [--->] IV_STRUC_NAME                  TYPE        STRING
* | [--->] IT_SEP_FIELD_VALUE             TYPE        TT_STRING
* | [<-->] CT_PARENT_LIST                 TYPE        TT_STRING
* | [<-->] CT_LINE                        TYPE        ANY
* +--------------------------------------------------------------------------------------</SIGNATURE>
  METHOD update_target_child_table.
    FIELD-SYMBOLS:
      <fs_table>  TYPE STANDARD TABLE,
      <fs_result> TYPE STANDARD TABLE.

    DATA:
      lo_table_ref TYPE REF TO data,
      lo_struc     TYPE REF TO data,
      lv_lines     TYPE i.

    DATA:
      lo_tabledescr TYPE REF TO cl_abap_tabledescr,
      lo_table      TYPE REF TO data.

    READ TABLE ct_parent_list INTO DATA(ls_parent_list) INDEX 1.
    IF sy-subrc = 0.
      ASSIGN COMPONENT ls_parent_list OF STRUCTURE ct_line TO <fs_table>.

      lv_lines = lines( <fs_table> ).
**********************************************************************
*     If filed name matched,append data to this node
**********************************************************************
      IF iv_struc_name = ls_parent_list.
        READ TABLE mt_line_type INTO DATA(ls_line_type)
          WITH KEY name = iv_struc_name.

        CREATE DATA lo_struc TYPE HANDLE ls_line_type-type.
        ASSIGN lo_struc->* TO FIELD-SYMBOL(<fs_new_struc>).

        LOOP AT it_sep_field_value INTO DATA(ls_field_value).
          ASSIGN COMPONENT sy-tabix OF STRUCTURE <fs_new_struc> TO FIELD-SYMBOL(<fs_field_value>).
          IF <fs_field_value> IS ASSIGNED.
            <fs_field_value> = ls_field_value.
          ENDIF.

          UNASSIGN <fs_field_value>.
        ENDLOOP.

        APPEND <fs_new_struc> TO <fs_table>.
        RETURN.
**********************************************************************
*     If not matched,Recursively search until the match is successful
**********************************************************************
      ELSE.
        DELETE ct_parent_list INDEX 1.

        READ TABLE <fs_table> ASSIGNING FIELD-SYMBOL(<fs_last_line>) INDEX lv_lines.

*       Update child note fields value
        update_target_child_table(
          EXPORTING
            iv_struc_name      = iv_struc_name
            it_sep_field_value = it_sep_field_value
          CHANGING
            ct_line            = <fs_last_line>
            ct_parent_list     = ct_parent_list
        ).
      ENDIF.
    ENDIF.
  ENDMETHOD.
ENDCLASS.

测试程序demo源码:

*&---------------------------------------------------------------------*
*& Report ZPRTEST_FOR_DEEPFILE
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*
REPORT zprtest_for_deepfile.

TYPES:
  BEGIN OF ty_details,
    type TYPE string,
    key  TYPE string,
    text TYPE string,
  END OF ty_details,
  tt_details TYPE STANDARD TABLE OF ty_details WITH NON-UNIQUE EMPTY KEY.

TYPES:
  BEGIN OF ty_items,
    type    TYPE string,
    key     TYPE string,
    posnr   TYPE string,
    matnr   TYPE string,
    details TYPE tt_details,
  END OF ty_items,
  tt_items TYPE STANDARD TABLE OF ty_items WITH NON-UNIQUE EMPTY KEY,

  BEGIN OF ty_head,
    type  TYPE string,
    key   TYPE string,
    bukrs TYPE string,
    vbeln TYPE string,
    items TYPE tt_items,
  END OF ty_head,
  tt_head TYPE STANDARD TABLE OF ty_head WITH NON-UNIQUE EMPTY KEY,

  BEGIN OF ty_data,
    head TYPE tt_head,
  END OF ty_data.

TYPES:
  BEGIN OF ty_source_data,
    data TYPE string,
  END OF ty_source_data,
  tt_source_data TYPE STANDARD TABLE OF ty_source_data,

  BEGIN OF ty_convert_rule,
    param TYPE string,
    value TYPE string,
  END OF ty_convert_rule,
  tt_convert_rule TYPE STANDARD TABLE OF ty_convert_rule.

DATA:
  lt_source_data  TYPE tt_source_data,
  lt_convert_rule TYPE tt_convert_rule,
  ls_result       TYPE REF TO data,
  lt_error        TYPE bapiret2_t.

DATA:
  lt_data TYPE ty_data.

FIELD-SYMBOLS:
  <fs_result> TYPE any.

lt_source_data  = VALUE #(
                  ( data = 'A012A010123456789' )
                  ( data = 'A0200010ABCDEFGHI12345678' )
                  ( data = 'A03TEXT' )
                  ( data = 'A012A010123456789' )
                  ( data = 'A0200010ABCDEFGHI12345678' )
                  ( data = 'A03TEXT1' )
                  ( data = 'A03TEXT2' )
                  ( data = 'A0200020ABCDEFGHI12345678' )
                ).

*正常解析
lt_convert_rule = VALUE #(
                  ( param = 'STRUCTURE'            value = 'HEAD,ITEMS,DETAILS' )
                  ( param = 'KEYFNAME'             value = 'KEY' )
                  ( param = 'HEAD.FIELDNAMES'      value = 'TYPE,KEY,BUKRS,VBELN' )
                  ( param = 'HEAD.FIELDLEN'        value = '1,2,4,10' )
                  ( param = 'HEAD.FIELDLENTYPE'    value = 'BYTE' )
                  ( param = 'HEAD.KEYVALUE'        value = '01' )
                  ( param = 'HEAD.PARENT'          value = 'ROOT' )

                  ( param = 'ITEMS.FIELDNAMES'     value = 'TYPE,KEY,POSNR,MATNR' )
                  ( param = 'ITEMS.FIELDLEN'       value = '1,2,6,18' )
                  ( param = 'ITEMS.FIELDLENTYPE'   value = 'BYTE' )
                  ( param = 'ITEMS.KEYVALUE'       value = '02' )
                  ( param = 'ITEMS.PARENT'         value = 'HEAD' )

                  ( param = 'DETAILS.FIELDNAMES'   value = 'TYPE,KEY,DESCRIP' )
                  ( param = 'DETAILS.FIELDLEN'     value = '1,2,20' )
                  ( param = 'DETAILS.FIELDLENTYPE' value = 'BYTE' )
                  ( param = 'DETAILS.KEYVALUE'     value = '03' )
                  ( param = 'DETAILS.PARENT'       value = 'ITEMS' )
                ).

*将item和details设为平级,位于head节点下
*lt_convert_rule = VALUE #(
*                  ( param = 'STRUCTURE'            value = 'HEAD,ITEMS,DETAILS' )
*                  ( param = 'KEYFNAME'             value = 'KEY' )
*                  ( param = 'HEAD.FIELDNAMES'      value = 'TYPE,KEY,BUKRS,VBELN' )
*                  ( param = 'HEAD.FIELDLEN'        value = '1,2,4,10' )
*                  ( param = 'HEAD.FIELDLENTYPE'    value = 'BYTE' )
*                  ( param = 'HEAD.KEYVALUE'        value = '01' )
*                  ( param = 'HEAD.PARENT'          value = 'ROOT' )
*
*                  ( param = 'ITEMS.FIELDNAMES'     value = 'TYPE,KEY,POSNR,MATNR' )
*                  ( param = 'ITEMS.FIELDLEN'       value = '1,2,6,18' )
*                  ( param = 'ITEMS.FIELDLENTYPE'   value = 'BYTE' )
*                  ( param = 'ITEMS.KEYVALUE'       value = '02' )
*                  ( param = 'ITEMS.PARENT'         value = 'HEAD' )
*
*                  ( param = 'DETAILS.FIELDNAMES'   value = 'TYPE,KEY,DESCRIP' )
*                  ( param = 'DETAILS.FIELDLEN'     value = '1,2,20' )
*                  ( param = 'DETAILS.FIELDLENTYPE' value = 'BYTE' )
*                  ( param = 'DETAILS.KEYVALUE'     value = '03' )
*                  ( param = 'DETAILS.PARENT'       value = 'HEAD' )
*                  ).

*只解析head数据
*lt_convert_rule = VALUE #(
*                  ( param = 'STRUCTURE'            value = 'HEAD' )
*                  ( param = 'KEYFNAME'             value = 'KEY' )
*                  ( param = 'HEAD.FIELDNAMES'      value = 'TYPE,KEY,BUKRS,VBELN' )
*                  ( param = 'HEAD.FIELDLEN'        value = '1,2,4,10' )
*                  ( param = 'HEAD.FIELDLENTYPE'    value = 'BYTE' )
*                  ( param = 'HEAD.KEYVALUE'        value = '01' )
*                  ( param = 'HEAD.PARENT'          value = 'ROOT' )
*                   ).

zprbccl_convert_file2table=>convert_file2table(
  EXPORTING
    it_source_data = lt_source_data
    it_convert_rule = lt_convert_rule
  IMPORTING
    es_result = ls_result
    et_error = lt_error
 ).

ASSIGN ls_result->* TO <fs_result>.

*lt_data = <fs_result>.

BREAK-POINT.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DeveloperMrMeng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值