前言
日本项目经常需要通过PI或者CPI去解析一些文件内容,如果只是扁平结构的,不涉及头行结构的话,在PI中可以FCC(File Content Conversion)进行解析转换,在CPI中可以使用groovy脚本进行解析转换,但是如果涉及头行层级关系的,PI中不支持通过FCC进行转换,但是可以通过一些自定义的转换Bean来进行转换,不过需要自己去编写java代码去实现,并导出为EAR文件部署到PI服务器上才可以,当时没有现成的java解析代码,考虑到实现这个功能的时间成本问题,最终选择在SAP端实现这个解析功能。
需求
源文件内容如下:
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格式https://blog.csdn.net/DeveloperMrMeng/article/details/130868786?spm=1001.2014.3001.5501
实现效果
1.测试数据内容:
2.解析规则参数传值:
重要参数说明:
STRUCTURE:包含哪几种类型的解析规则。
KEYFNAME:每种类型的判定依据,该字段要在每种类型的结构中都有。
<类型n>.FIELDNAMES:该类型包含哪些字段。
<类型n>.FIELDLEN:每个字段的长度,框架会按照指定长度去解析。
<类型n>.FIELDLENTYPE:解析长度的类型,BYTE为按字节长度解析,STRING为按照字符长度,不指定默认为STRING。
<类型n>.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 zprbcestring,
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 = 'ZPRBCESTRING'
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.