做接口XML和JSON的选择以及处理XML的几个函数

ABAP和外部系统做接口的时候,XML和JSON是两种常用的数据传输格式,它们各自有一些优缺点,如下所述:

XML:

优点:XML具有较好的可扩展性和灵活性,可以通过定义DTD或XSD等文档类型定义来定义和验证数据结构。XML的标记语言具有很强的可读性,易于人们理解和处理,对于需要手动编辑的文件而言,这是一个重要的优势。

缺点:XML语法比JSON复杂,需要更多的字符和标签来表示相同的信息,这导致XML的文件大小较大。在处理大型数据时,会带来较高的处理负载和内存占用。与JSON相比,XML处理速度较慢。

JSON:

优点:JSON比XML更轻量级,因此在传输和处理大量数据时更有效率。语法简单,易于阅读和编写,并且可以通过各种编程语言和平台进行处理。

缺点:JSON缺乏XML的严格数据类型,无法通过DTD或XSD等文档类型定义来验证数据结构的正确性。不支持注释,这使得JSON文件不像XML那样容易被人类读取和编辑,对于需要手动编辑的文件而言,JSON的可读性不如XML。

实事求是的讲,就ABAP做的接口而言,大多都是固定的数据格式,不需要人工解读,DTD和XSD在接口使用场景下更是毫无用处,所以XML的优点在这儿无任何卵用,反而是文件大、效率低、处理起来恶心,相比JSON一无是处。

所以建议使用JSON来代替XML。

正是因为XML处理起来太麻烦,所以给大家提供几个有用的函数,有了这几个函数足矣应付90+%的XML格式接口场景了。

1、SAP数据转为XML文件

FUNCTION zxml_data_to_string .
*"----------------------------------------------------------------------
*"*"局部接口:
*"  IMPORTING
*"     REFERENCE(DATA) TYPE  ANY
*"     REFERENCE(CASE) TYPE  C OPTIONAL
*"     REFERENCE(PRETTY) TYPE  C OPTIONAL
*"     REFERENCE(ROOTNAME) TYPE  STRING DEFAULT 'BOOT'
*"     REFERENCE(CHARACTER_SET) TYPE  STRING DEFAULT 'UTF-8'
*"     REFERENCE(DCXMLINIT) TYPE  DCXMLINIT DEFAULT 'N'
*"  EXPORTING
*"     REFERENCE(XMLSTR) TYPE  STRING
*"     REFERENCE(XMLXSTR) TYPE  XSTRING
*"  TABLES
*"      MAPTAB STRUCTURE  ZXML_NODEN_MAPPING OPTIONAL
*"  EXCEPTIONS
*"      DATA_TO_DOM_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 node     TYPE REF TO if_ixml_node.
  DATA iterator TYPE REF TO if_ixml_node_iterator.
  DATA ostream  TYPE REF TO if_ixml_ostream.
  DATA encoding TYPE REF TO if_ixml_encoding .
  DATA element  TYPE REF TO if_ixml_element.
  DATA retval   TYPE i.
  DATA str      TYPE string.
  DATA control  TYPE  dcxmlsercl.


  ifxml    = cl_ixml=>create( ).
  document = ifxml->create_document( ).
  factory  = ifxml->create_stream_factory( ).
  encoding = ifxml->create_encoding( byte_order = 0 character_set = character_set ).


  control-init_treat = dcxmlinit .


  CALL FUNCTION 'SDIXML_DATA_TO_DOM'
    EXPORTING
      name         = rootname
      dataobject   = data
      control      = control
    IMPORTING
      data_as_dom  = element
    CHANGING
      document     = document
    EXCEPTIONS
      illegal_name = 1
      OTHERS       = 2.
  IF element IS INITIAL.
    RAISE data_to_dom_error.
  ELSE.
    retval = document->append_child( new_child = element ).
  ENDIF.




  IF case <> '' OR maptab[] IS NOT INITIAL.
    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.
        READ TABLE maptab WITH KEY frstr = node->get_name( ).
        IF sy-subrc = 0.
          str = maptab-tostr.
          node->set_name( str ).
        ELSE.
          CASE case.
            WHEN 'U'.
              node->set_name( to_upper( node->get_name( ) ) ).
            WHEN 'L'.
              node->set_name( to_lower( node->get_name( ) ) ).
            WHEN OTHERS.
          ENDCASE.
        ENDIF.
      ENDIF.
    ENDDO.
  ENDIF.


  ostream = factory->create_ostream_xstring( string = xmlxstr ).
  ostream->set_encoding( encoding = encoding ).
  ostream->set_pretty_print( pretty_print = pretty ).
  document->render( ostream = ostream ).
  xmlstr = cl_abap_codepage=>convert_from( source = xmlxstr codepage = character_set ).


ENDFUNCTION.

2、XML转为SAP数据

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
*"----------------------------------------------------------------------
***如果要取CDATA数据
***程序:LSDIXMLF04,FORM get_cdata,做如下修改:
*  ...
*  DATA: icdata.
*
*  CLEAR value.
*  IMPORT icdata FROM MEMORY ID 'ZXML_STRING_TO_DATA_ICDATA'.
*  IF icdata IS NOT INITIAL .
*    filter = element->create_filter_node_type( 48 ).
*  ELSE.
*    filter = element->create_filter_node_type(
*                  if_ixml_node=>co_node_text ).
*  ENDIF.
*  iterator = element->create_iterator( 1 ).
*  ...


  DATA: go_xml TYPE REF TO cl_xml_document .


  IF icdata IS NOT INITIAL.
    EXPORT icdata TO MEMORY ID 'ZXML_STRING_TO_DATA_ICDATA'. "LSDIXMLF04
  ENDIF.


  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.


  FREE MEMORY ID 'ZXML_STRING_TO_DATA_ICDATA'.
ENDFUNCTION.

3、对XML重新处理,比如标签名的批量更改、字符集的转换等

FUNCTION zxml_string_rebuild .
*"----------------------------------------------------------------------
*"*"局部接口:
*"  IMPORTING
*"     REFERENCE(XMLIN) TYPE  STRING
*"     REFERENCE(CASE) TYPE  C OPTIONAL
*"     REFERENCE(PRETTY) TYPE  C OPTIONAL
*"     REFERENCE(CHARACTER_SET) TYPE  STRING DEFAULT 'UTF-8'
*"  EXPORTING
*"     VALUE(XMLOUT) TYPE  STRING
*"     VALUE(XMLOUTX) TYPE  XSTRING
*"     VALUE(CDATA) TYPE  RSTT_T_STRINGS
*"  TABLES
*"      MAPTAB STRUCTURE  ZXML_NODEN_MAPPING OPTIONAL
*"  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 retval   TYPE i.
  DATA xmlxstr  TYPE xstring.
  DATA str      TYPE string.
  DATA wa_cdata TYPE rstt_s_string.


  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.
      READ TABLE maptab WITH KEY frstr = node->get_name( ).
      IF sy-subrc = 0.
        str = maptab-tostr.
        node->set_name( str ).
      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.


  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.

4、XML表示数组(内表)有两种格式,分别是

ab192edbc1b9afe12e60eb45d1a9894c.png

和:

c8e8a71b0bf4367959550c02fdfcd4e9.png

SAP默认生成是第二种格式,但是如果外部系统只认第一种格式呢?

这个时候就需要有把两个格式相互转换的方法,也就是下面两个函数:

FUNCTION ZXML_TRANSFER_ITEMTAG1 .
*"----------------------------------------------------------------------
*"*"局部接口:
*"  IMPORTING
*"     VALUE(XMLIN) TYPE  STRING
*"  EXPORTING
*"     VALUE(XMLSTR) TYPE  STRING
*"  TABLES
*"      TAGTAB
*"----------------------------------------------------------------------
  DATA: maptab TYPE TABLE OF zxml_noden_mapping WITH HEADER LINE.
  DATA: tagc(100).
  DATA: moff TYPE i .


  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.
  LOOP AT tagtab.
    CLEAR: maptab,maptab[].
    maptab-frstr = tagtab.
    maptab-tostr = 'item'.
    APPEND maptab.


    CALL FUNCTION 'ZXML_STRING_REBUILD'
      EXPORTING
        xmlin         = xmlstr
        character_set = ''
      IMPORTING
        xmlout        = xmlstr
      TABLES
        maptab        = maptab
      EXCEPTIONS
        xml_error     = 1.
    IF sy-subrc <> 0.
      MESSAGE e000(oo) WITH '转换出错'.
    ELSE.
      FIND FIRST OCCURRENCE OF '<item>' IN SECTION OFFSET moff OF xmlstr
                                        MATCH OFFSET moff.
      IF sy-subrc = 0.
        tagc = '<' && tagtab && '>'.
        CONCATENATE xmlstr(moff) tagc xmlstr+moff INTO xmlstr.
      ENDIF.


      FIND ALL OCCURRENCES OF '</item>' IN SECTION OFFSET moff OF xmlstr
                                        MATCH OFFSET moff.
      IF sy-subrc = 0.
        moff = moff + 7.
        tagc = '</' && tagtab && '>'.
        CONCATENATE xmlstr(moff) tagc xmlstr+moff INTO xmlstr.
      ENDIF.
    ENDIF.
  ENDLOOP.


ENDFUNCTION.
FUNCTION zxml_transfer_itemtag2 .
*"----------------------------------------------------------------------
*"*"局部接口:
*"  IMPORTING
*"     VALUE(XMLIN) TYPE  STRING
*"  EXPORTING
*"     VALUE(XMLSTR) TYPE  STRING
*"  TABLES
*"      TAGTAB
*"----------------------------------------------------------------------
  DATA: regex(100).
  DATA: tagb(100),
        tage(100),
        tags(100).
  DATA: moff TYPE i ,
        mlen TYPE i .


  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.
  LOOP AT tagtab.
    tagb =  '<' && tagtab && '>'.
    tage =  '</' && tagtab && '>'.
    regex = tagb && '.*' && tage.


    FIND FIRST OCCURRENCE OF REGEX regex IN xmlstr MATCH OFFSET moff MATCH LENGTH mlen  .
    IF sy-subrc = 0.
      REPLACE ALL OCCURRENCES OF '<item>' IN SECTION OFFSET moff LENGTH mlen OF xmlstr WITH tagb.
    ENDIF.


    FIND FIRST OCCURRENCE OF REGEX regex IN xmlstr MATCH OFFSET moff MATCH LENGTH mlen  .
    IF sy-subrc = 0.
      REPLACE ALL OCCURRENCES OF '</item>' IN SECTION OFFSET moff LENGTH mlen OF xmlstr WITH tage.
    ENDIF.


    tags = tagb && tagb.
    REPLACE ALL OCCURRENCES OF tags IN xmlstr WITH tagb.
    tags = tage && tage.
    REPLACE ALL OCCURRENCES OF tags IN xmlstr WITH tage.
  ENDLOOP.


ENDFUNCTION.

测试样例:

REPORT z_barry_testxml NO STANDARD PAGE HEADING.


DATA: BEGIN OF wa_appaysavz  ,
        errcod TYPE string,
        errmsg TYPE string,
        paytim TYPE string,
        recnum TYPE string,
        refnbr TYPE string,
        remark TYPE string,
      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,
      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_cbserppgk ,
        info      LIKE wa_info,
        appaysavy LIKE TABLE OF wa_appaysavy,
        appaysavz LIKE TABLE OF wa_appaysavz,
        sycomretz LIKE wa_sycomretz,
      END OF wa_cbserppgk.


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"?>'
              '<CBSERPPGK>'
              '  <INFO>'
              '    <ERPTYP>H</ERPTYP>'
              '    <ERRMSG/>'
              '    <FUNNAM>ERPAYSAV</FUNNAM>'
              '    <RETCOD>0000000</RETCOD>'
              '  </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:FK21092321147</REFNBR>'
              '    <REMARK/>'
              '  </APPAYSAVZ>'
              '  <APPAYSAVZ>'
              '    <ERRCOD>0000001</ERRCOD>'
              '    <ERRMSG>业务参考号重复!</ERRMSG>'
              '    <PAYTIM/>'
              '    <RECNUM>1</RECNUM>'
              '    <REFNBR>gp:FK21092321148</REFNBR>'
              '    <REMARK/>'
              '  </APPAYSAVZ>'
              '  <SYCOMRETZ>'
              '    <ERRCOD>0000000</ERRCOD>'
              '    <ERRDTL/>'
              '    <ERRMSG/>'
              '  </SYCOMRETZ>'
              '</CBSERPPGK>'
      INTO xmlstr.


  "转换内表的标签格式
  APPEND 'APPAYSAVY' TO tagtab.
  APPEND 'APPAYSAVZ' TO tagtab.
  CALL FUNCTION 'ZXML_TRANSFER_ITEMTAG1'
    EXPORTING
      xmlin  = xmlstr
    IMPORTING
      xmlstr = xmlstr
    TABLES
      tagtab = tagtab.


  "反序列化
  CALL FUNCTION 'ZXML_STRING_TO_DATA'
    EXPORTING
      xmlstr = xmlstr
    IMPORTING
      data   = wa_cbserppgk.


  BREAK-POINT.

几个特别重要的说明:

1、使用ZXML_STRING_TO_DATA反序列化XML的时候,SAP对象的元素定义和XML的格式并不需要一一对应。比如银行接口,针对不同的业务会返回不同格式的XML,这个时候只要定义一个包含这些格式元素全集的深层结构就OK了。

2、有时候需要直接发送二进制的XML给外部系统或者是下载到本地,ZXML_DATA_TO_STRING和ZXML_STRING_REBUILD都提供XSTRING格式的XML,在需要的时候,可以直接使用而不必再转换一次。

3、ZXML_TRANSFER_ITEMTAG1和2本来应该写成一个单独的函数,但这玩意用得少,没动力,不改了。

4、有时候外部系统会把数据写到CDATA里面,ZXML_STRING_TO_DATA函数是不能直接取到的,需要改一下源码才可,改源码的位置和代码写到ZXML_STRING_TO_DATA的注释里面了。

3a4888d105785f496d6b02538d2c3357.jpeg

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值