此方法参考ABAP老白原函数基础上进行改造,支持更多Xml格式文件处理。
原文来自https://mp.weixin.qq.com/s/NfnAFYI11xtHyRz6Q8eAaw
XML表示数组(内表)有两种格式,分别是
和
对于SAP来说默认生成第二种格式,但是如果外部系统只提供第一种格式的话,就需要我们进行处理了。日常SAP处理Xml非常麻烦,因为当时我开发需求中Xml格式非常复杂,所以老白的函数不支持处理此类Xml文件,我在此基础上对函数进行了调整,并进行了一些优化,可以支持比较复杂的Xml文件处理,包括多级深层结构,且支持不同父级标签包含相同标签名的子标签等,应该能支持95%的Xml情况,唯一的问题可能是当Xml特别大的时候,可能处理会非常慢,开发这个函数的时候需要处理的Xml文件有30M左右,需要替换的标签高达10W条,所以处理非常慢,可能不适合特别大的xml文件,支持正常的Xml文件来说还是非常好用。
ZXML_TRANSFER_ITEMTAG3
FUNCTION zxml_transfer_itemtag3.
*"----------------------------------------------------------------------
*"*"本地接口:
*" IMPORTING
*" REFERENCE(XMLIN) TYPE STRING
*" EXPORTING
*" REFERENCE(XMLSTR) TYPE STRING
*" TABLES
*" TAGTAB STRUCTURE ZXML_NODEN_MAPPING
*"----------------------------------------------------------------------
DATA: maptab TYPE TABLE OF zxml_noden_mapping.
DATA: maptab1 TYPE TABLE OF zxml_noden_mapping.
FIELD-SYMBOLS:<fs_maptab> TYPE zxml_noden_mapping.
DATA: tagc(100).
DATA: moff TYPE i .
DATA ifxml TYPE REF TO if_ixml.
DATA factory TYPE REF TO if_ixml_stream_factory.
DATA document TYPE REF TO if_ixml_document.
DATA xmlxstr TYPE xstring.
DATA istream TYPE REF TO if_ixml_istream.
DATA parser TYPE REF TO if_ixml_parser.
DATA:lv_index TYPE sy-tabix.
DATA:lv_itext TYPE char50.
DATA:lv_otext TYPE char50.
CHECK xmlin IS NOT INITIAL.
FIND FIRST OCCURRENCE OF '<item>' IN xmlin .
IF sy-subrc = 0.
MESSAGE e000(oo) WITH '原始XML已经包含<item>标签,无法转换'.
ENDIF.
xmlstr = xmlin.
maptab = VALUE #( FOR ls_tagtab IN tagtab
(
frstr = ls_tagtab-frstr
* fastr = ls_tagtab-fastr
* fastr1 = ls_tagtab-fastr1
tostr = 'ITEM' && ls_tagtab-frstr
)
).
CALL FUNCTION 'ZXML_STRING_REBUILD1'
EXPORTING
xmlin = xmlstr
character_set = ''
IMPORTING
xmlout = xmlstr
TABLES
maptab = maptab
maptab1 = maptab1
EXCEPTIONS
xml_error = 1.
IF sy-subrc <> 0.
MESSAGE e000(oo) WITH '转换出错'.
ENDIF.
"动态替换
SORT maptab1 BY frstr tostr."去重
DELETE ADJACENT DUPLICATES FROM maptab1 COMPARING frstr tostr.
REFRESH maptab.
DESCRIBE TABLE maptab1 LINES gv_lines.
GET TIME STAMP FIELD gv_start.
CLEAR:gv_tabix.
LOOP AT maptab1 ASSIGNING <fs_maptab>.
ADD 1 TO gv_tabix.
PERFORM frm_indicator USING gv_tabix.
CLEAR moff.
lv_itext = '<' && <fs_maptab>-tostr && '>'.
lv_otext = '</' && <fs_maptab>-tostr && '>'.
FIND FIRST OCCURRENCE OF REGEX lv_itext IN SECTION OFFSET moff OF xmlstr
MATCH OFFSET moff.
IF sy-subrc = 0.
tagc = '<' && <fs_maptab>-frstr && '>'.
CONCATENATE xmlstr(moff) tagc xmlstr+moff INTO xmlstr.
ENDIF.
FIND ALL OCCURRENCES OF REGEX lv_otext IN SECTION OFFSET moff OF xmlstr
MATCH OFFSET moff.
IF sy-subrc = 0.
lv_index = cl_abap_list_utilities=>dynamic_output_length( lv_otext ).
moff = moff + lv_index.
tagc = '</' && <fs_maptab>-frstr && '>'.
CONCATENATE xmlstr(moff) tagc xmlstr+moff INTO xmlstr.
ENDIF.
ENDLOOP.
REPLACE ALL OCCURRENCES OF REGEX '<ITEM\w+>' IN xmlstr WITH '<item>'.
REPLACE ALL OCCURRENCES OF REGEX '</ITEM\w+>' IN xmlstr WITH '</item>'.
"检查转换是否正确
ifxml = cl_ixml=>create( ).
document = ifxml->create_document( ).
factory = ifxml->create_stream_factory( ).
xmlxstr = cl_abap_codepage=>convert_to( source = xmlin ).
istream = factory->create_istream_xstring( string = xmlxstr ).
parser = ifxml->create_parser( document = document
stream_factory = factory
istream = istream
).
IF parser->parse( ) <> 0.
MESSAGE e000(oo) WITH '转换出错'.
ENDIF.
ENDFUNCTION.
ZXML_STRING_REBUILD1
FUNCTION zxml_string_rebuild1.
*"----------------------------------------------------------------------
*"*"本地接口:
*" IMPORTING
*" REFERENCE(XMLIN) TYPE STRING
*" REFERENCE(CASE) TYPE C OPTIONAL
*" REFERENCE(PRETTY) TYPE C OPTIONAL
*" REFERENCE(CHARACTER_SET) TYPE STRING DEFAULT 'UTF-8'
*" EXPORTING
*" REFERENCE(XMLOUT) TYPE STRING
*" REFERENCE(XMLOUTX) TYPE XSTRING
*" REFERENCE(CDATA) TYPE RSTT_T_STRINGS
*" TABLES
*" MAPTAB STRUCTURE ZXML_NODEN_MAPPING
*" MAPTAB1 STRUCTURE ZXML_NODEN_MAPPING
*" EXCEPTIONS
*" XML_ERROR
*"----------------------------------------------------------------------
DATA ifxml TYPE REF TO if_ixml.
DATA factory TYPE REF TO if_ixml_stream_factory.
DATA document TYPE REF TO if_ixml_document.
DATA iterator TYPE REF TO if_ixml_node_iterator.
DATA node TYPE REF TO if_ixml_node.
DATA parser TYPE REF TO if_ixml_parser.
DATA istream TYPE REF TO if_ixml_istream.
DATA ostream TYPE REF TO if_ixml_ostream.
DATA encoding TYPE REF TO if_ixml_encoding .
DATA:lv_node TYPE REF TO if_ixml_node."父节点
DATA xmlxstr TYPE xstring.
DATA str TYPE string.
DATA wa_cdata TYPE rstt_s_string.
DATA:lv_name TYPE string.
DATA:lv_name1 TYPE string.
DATA:lv_index TYPE i.
ifxml = cl_ixml=>create( ).
document = ifxml->create_document( ).
factory = ifxml->create_stream_factory( ).
xmlxstr = cl_abap_codepage=>convert_to( source = xmlin ).
istream = factory->create_istream_xstring( string = xmlxstr ).
parser = ifxml->create_parser( document = document
stream_factory = factory
istream = istream
).
IF parser->parse( ) <> 0.
RAISE xml_error.
ENDIF.
iterator = document->create_iterator( ).
DO.
node = iterator->get_next( ).
IF node IS INITIAL.
EXIT.
ENDIF.
IF node->get_type( ) = if_ixml_node=>co_node_element.
lv_name = node->get_name( )."替换为大写再比较字段名
TRANSLATE lv_name TO UPPER CASE .
READ TABLE maptab WITH KEY frstr = lv_name.
IF sy-subrc = 0.
lv_node = node->get_parent( )."获取父节点
lv_name1 = lv_node->get_name( ).
IF lv_node IS INITIAL.
str = maptab-tostr.
node->set_name( str ).
MOVE-CORRESPONDING maptab TO maptab1.
APPEND maptab1 TO maptab1.
EXIT.
ENDIF.
lv_index = lv_node->get_gid( )."返回父级列表Gid(唯一)
str = maptab-tostr && lv_index.
node->set_name( str ).
MOVE-CORRESPONDING maptab TO maptab1.
maptab1-tostr = str.
APPEND maptab1 TO maptab1.
CLEAR maptab1.
ELSE.
* CASE case.
* WHEN 'U'.
node->set_name( to_upper( node->get_name( ) ) )."标签转大写
* WHEN 'L'.
* node->set_name( to_lower( node->get_name( ) ) ).
* ENDCASE.
ENDIF.
ELSEIF node->get_type( ) = if_ixml_node=>co_node_cdata_section.
wa_cdata-string = node->get_value( ).
APPEND wa_cdata TO cdata.
ENDIF.
ENDDO.
* BREAK-POINT.
ostream = factory->create_ostream_xstring( string = xmloutx ).
IF character_set IS NOT INITIAL.
encoding = ifxml->create_encoding( byte_order = 0 character_set = character_set ).
ostream->set_encoding( encoding = encoding ).
ENDIF.
IF pretty IS NOT INITIAL.
ostream->set_pretty_print( pretty_print = pretty ).
ENDIF.
document->render( ostream = ostream ).
xmlout = cl_abap_codepage=>convert_from( xmloutx ).
ENDFUNCTION.
ZXML_STRING_TO_DATA
FUNCTION zxml_string_to_data.
*"----------------------------------------------------------------------
*"*"本地接口:
*" IMPORTING
*" REFERENCE(XMLSTR) TYPE STRING
*" REFERENCE(ICDATA) TYPE CHAR1 OPTIONAL
*" EXPORTING
*" REFERENCE(DATA) TYPE ANY
*" REFERENCE(SUBRC) TYPE SY-SUBRC
*"----------------------------------------------------------------------
DATA: go_xml TYPE REF TO cl_xml_document .
IF go_xml IS INITIAL.
CREATE OBJECT go_xml.
ENDIF.
CALL METHOD go_xml->parse_string
EXPORTING
stream = xmlstr
RECEIVING
retcode = subrc.
CALL METHOD go_xml->get_data
IMPORTING
retcode = subrc
CHANGING
dataobject = data.
ENDFUNCTION.
下面是简单的测试程序,
*&---------------------------------------------------------------------*
*& Report Z_TEST_DNE1
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*
REPORT zdemo_04.
*Address
DATA: BEGIN OF wa_address ,
streetname TYPE char100,
number TYPE char100,
additionaladdressdetail TYPE char100,
building TYPE char100,
city TYPE char100,
postalcode TYPE char100,
region TYPE char100,
country TYPE char100,
addresstype TYPE char100,
END OF wa_address.
*companystructure
DATA: BEGIN OF wa_companystructure ,
registrationnumber TYPE char100,
name TYPE char100,
address LIKE TABLE OF wa_address,
END OF wa_companystructure.
*masterfiles-Customers-Customer
DATA: BEGIN OF wa_customer ,
companystructure LIKE TABLE OF wa_companystructure, "下一级
customerid TYPE char100,
selfbillingindicator TYPE char100,
accountid TYPE char100,
openingdebitbalance TYPE char100,
openingcreditbalance TYPE char100,
closingdebitbalance TYPE char100,
closingcreditbalance TYPE char100,
END OF wa_customer.
DATA: BEGIN OF wa_supplier ,
companystructure LIKE TABLE OF wa_companystructure, "下一级
supplierid TYPE char100,
selfbillingindicator TYPE char100,
accountid TYPE char100,
openingdebitbalance TYPE char100,
openingcreditbalance TYPE char100,
closingdebitbalance TYPE char100,
closingcreditbalance TYPE char100,
END OF wa_supplier.
*masterfiles-Customers
DATA: BEGIN OF wa_customers ,
customer LIKE TABLE OF wa_customer, "下一级
END OF wa_customers.
*masterfiles-Suppliers
DATA: BEGIN OF wa_suppliers ,
supplier LIKE TABLE OF wa_supplier, "下一级
END OF wa_suppliers.
DATA: BEGIN OF wa_err ,
err1 TYPE string,
err2 TYPE string,
END OF wa_err.
DATA: BEGIN OF wa_appaysavz ,
errcod TYPE string,
errmsg TYPE string,
paytim TYPE string,
recnum TYPE string,
refnbr TYPE string,
remark TYPE string,
err LIKE TABLE OF wa_err,
END OF wa_appaysavz.
DATA: BEGIN OF wa_appaysavy ,
busnbr TYPE string,
exttx1 TYPE string,
sqrnbr TYPE string,
END OF wa_appaysavy.
DATA: BEGIN OF wa_info ,
erptyp TYPE string,
errmsg TYPE string,
funnam TYPE string,
retcod TYPE string,
err LIKE TABLE OF wa_err,
END OF wa_info.
DATA: BEGIN OF wa_sycomretz,
errcod TYPE string,
errdtl TYPE string,
errmsg TYPE string,
END OF wa_sycomretz.
DATA: BEGIN OF wa_masterfiles ,
info LIKE TABLE OF wa_info,
appaysavy LIKE TABLE OF wa_appaysavy,
appaysavz LIKE TABLE OF wa_appaysavz,
sycomretz LIKE wa_sycomretz,
customers LIKE TABLE OF wa_customers, "下一级
suppliers LIKE TABLE OF wa_suppliers, "下一级
END OF wa_masterfiles.
DATA: BEGIN OF wa_auditfile ,
masterfiles LIKE TABLE OF wa_masterfiles, "下一级
END OF wa_auditfile.
DATA:gt_mapping TYPE TABLE OF zxml_noden_mapping,
gs_mapping TYPE zxml_noden_mapping.
DATA moff TYPE i .
DATA xmlstr TYPE string.
*DATA maptab TYPE TABLE OF zxml_noden_mapping WITH HEADER LINE.
DATA tagtab TYPE TABLE OF char100.
START-OF-SELECTION.
CONCATENATE '<?xml version="1.0" encoding="UTF-8"?>'
'<AuditFile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="mfp:anaf:dgti:d406:declaratie:v1" xsi:schemaLocation="urn:StandardAuditFile-Taxation-Financial:RO Romanian_SAF-T_Financial_Schema_v_2.1.xsd">'
'<MASTERFILES>'
' <INFO>'
' <ERPTYP>H</ERPTYP>'
' <ERRMSG/>'
' <FUNNAM>ERPAYSAV</FUNNAM>'
' <RETCOD>0000000</RETCOD>'
' <ERR>'
' <ERR1>0000001</ERR1>'
' <ERR2>0000002</ERR2>'
' </ERR>'
' <ERR>'
' <ERR1>0000003</ERR1>'
' <ERR2>0000004</ERR2>'
' </ERR>'
' </INFO>'
' <APPAYSAVY>'
' <BUSNBR>0000001</BUSNBR>'
' <EXTTX1>0000001</EXTTX1>'
' <SQRNBR>0000001</SQRNBR>'
' </APPAYSAVY>'
' <APPAYSAVY>'
' <BUSNBR>0000002</BUSNBR>'
' <EXTTX1>0000002</EXTTX1>'
' <SQRNBR>0000002</SQRNBR>'
' </APPAYSAVY>'
' <APPAYSAVY>'
' <BUSNBR>0000003</BUSNBR>'
' <EXTTX1>0000003</EXTTX1>'
' <SQRNBR>0000003</SQRNBR>'
' </APPAYSAVY>'
' <APPAYSAVZ>'
' <ERRCOD>0000001</ERRCOD>'
' <ERRMSG>业务参考号重复!</ERRMSG>'
' <PAYTIM/>'
' <RECNUM>1</RECNUM>'
' <REFNBR>gp:FK21092321148</REFNBR>'
' <REMARK/>'
' <ERR>'
' <ERR1>0000001</ERR1>'
' <ERR2>0000002</ERR2>'
' </ERR>'
' <ERR>'
' <ERR1>0000003</ERR1>'
' <ERR2>0000004</ERR2>'
' </ERR>'
' </APPAYSAVZ>'
' <SYCOMRETZ>'
' <ERRCOD>0000000</ERRCOD>'
' <ERRDTL/>'
' </SYCOMRETZ>'
' <CUSTOMERS>'
' <CUSTOMER>'
'<COMPANYSTRUCTURE>'
'<REGISTRATIONNUMBER>0010102512</REGISTRATIONNUMBER>'
'<NAME>ELTON CORPORATION SA</NAME>'
'<ADDRESS>'
'<STREETNAME>CAMPULUI</STREETNAME>'
'<NUMBER>5</NUMBER>'
'<CITY>PANTELIMON </CITY>'
'<POSTALCODE>NULL</POSTALCODE>'
'<COUNTRY>RO</COUNTRY>'
'<ADDRESSTYPE>STREETADDRESS</ADDRESSTYPE>'
'</ADDRESS>'
'</COMPANYSTRUCTURE>'
'<CUSTOMERID>0010102512</CUSTOMERID>'
'<SELFBILLINGINDICATOR>0</SELFBILLINGINDICATOR>'
'<ACCOUNTID>4111</ACCOUNTID>'
'<OPENINGCREDITBALANCE>0.00</OPENINGCREDITBALANCE>'
'<CLOSINGCREDITBALANCE>0.00</CLOSINGCREDITBALANCE>'
'</CUSTOMER>'
' <CUSTOMER>'
'<COMPANYSTRUCTURE>'
'<REGISTRATIONNUMBER>0010102512</REGISTRATIONNUMBER>'
'<NAME>ELTON CORPORATION SA</NAME>'
'<ADDRESS>'
'<STREETNAME>CAMPULUI</STREETNAME>'
'<NUMBER>5</NUMBER>'
'<CITY>PANTELIMON </CITY>'
'<POSTALCODE>NULL</POSTALCODE>'
'<COUNTRY>RO</COUNTRY>'
'<ADDRESSTYPE>STREETADDRESS</ADDRESSTYPE>'
'</ADDRESS>'
'</COMPANYSTRUCTURE>'
'<CUSTOMERID>0010102512</CUSTOMERID>'
'<SELFBILLINGINDICATOR>0</SELFBILLINGINDICATOR>'
'<ACCOUNTID>4111</ACCOUNTID>'
'<OPENINGCREDITBALANCE>0.00</OPENINGCREDITBALANCE>'
'<CLOSINGCREDITBALANCE>0.00</CLOSINGCREDITBALANCE>'
'</CUSTOMER>'
' </CUSTOMERS>'
'<SUPPLIERS>'
'<SUPPLIER>'
'<COMPANYSTRUCTURE>'
'<REGISTRATIONNUMBER>0010556705</REGISTRATIONNUMBER>'
'<NAME>ROMTECHNOLOGY TRANS-VAMAL SRL</NAME>'
'<ADDRESS>'
'<STREETNAME>STR.ALEXANDRU CEL BUN NR.25</STREETNAME>'
'<CITY>CONSTANTA</CITY>'
'<POSTALCODE>NULL</POSTALCODE>'
'<COUNTRY>RO</COUNTRY>'
'<ADDRESSTYPE>STREETADDRESS</ADDRESSTYPE>'
'</ADDRESS>'
'</COMPANYSTRUCTURE>'
'<SUPPLIERID>0010556705</SUPPLIERID>'
'<SELFBILLINGINDICATOR>0</SELFBILLINGINDICATOR>'
'<ACCOUNTID>401</ACCOUNTID>'
'<OPENINGCREDITBALANCE>0.00</OPENINGCREDITBALANCE>'
'<CLOSINGCREDITBALANCE>0.00</CLOSINGCREDITBALANCE>'
'</SUPPLIER>'
'<SUPPLIER>'
'<COMPANYSTRUCTURE>'
'<REGISTRATIONNUMBER>0010556705</REGISTRATIONNUMBER>'
'<NAME>ROMTECHNOLOGY TRANS-VAMAL SRL</NAME>'
'<ADDRESS>'
'<STREETNAME>STR.ALEXANDRU CEL BUN NR.25</STREETNAME>'
'<CITY>CONSTANTA</CITY>'
'<POSTALCODE>NULL</POSTALCODE>'
'<COUNTRY>RO</COUNTRY>'
'<ADDRESSTYPE>STREETADDRESS</ADDRESSTYPE>'
'</ADDRESS>'
'</COMPANYSTRUCTURE>'
'<SUPPLIERID>0010556705</SUPPLIERID>'
'<SELFBILLINGINDICATOR>0</SELFBILLINGINDICATOR>'
'<ACCOUNTID>401</ACCOUNTID>'
'<OPENINGCREDITBALANCE>0.00</OPENINGCREDITBALANCE>'
'<CLOSINGCREDITBALANCE>0.00</CLOSINGCREDITBALANCE>'
'</SUPPLIER>'
'</SUPPLIERS>'
'</MASTERFILES>'
'</AuditFile>'
INTO xmlstr.
DEFINE insert_tag.
gs_mapping-frstr = &1.
* gs_mapping-fastr = &2."上级节点
* gs_mapping-fastr1 = &3."再上一级节点
APPEND gs_mapping to gt_mapping.
CLEAR gs_mapping.
END-OF-DEFINITION.
"所有表结构
CLEAR:gt_mapping.
insert_tag :
'MASTERFILES',
'INFO',
'ERR',
'APPAYSAVY' ,
'APPAYSAVZ' ,
'CUSTOMERS',
'CUSTOMER',
'SUPPLIERS' ,
'SUPPLIER' ,
'COMPANYSTRUCTURE',
'ADDRESS'.
CALL FUNCTION 'ZXML_TRANSFER_ITEMTAG3'
EXPORTING
xmlin = xmlstr
IMPORTING
xmlstr = xmlstr
TABLES
tagtab = gt_mapping.
"反序列化
CALL FUNCTION 'ZXML_STRING_TO_DATA'
EXPORTING
xmlstr = xmlstr
IMPORTING
data = wa_auditfile.
BREAK-POINT.
使用ZXML_STRING_TO_DATA反序列化XML的时候,SAP对象的元素定义和XML的格式并不需要一一对应。比如银行接口,针对不同的业务会返回不同格式的XML,这个时候只要定义一个包含这些格式元素全集的深层结构就OK了