sap restful json接口动态调用RFC,生成动态结构
Posted 想发财的小夏
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了sap restful json接口动态调用RFC,生成动态结构相关的知识,希望对你有一定的参考价值。
需求:项目上有一个restful接口需求,想着能不能做成动态的,只发布一个sap的restful接口出去,然后外围系统根据不同的json来动态调用sap的rfc。
结果:结合同事以前的开发demo和abapgit上面大佬的abap_fm_json demo,写了一个简易的demo,如下代码:
METHOD http_broker.
DATA: ptab TYPE abap_func_parmbind_tab,
ptab_line TYPE abap_func_parmbind,
etab TYPE abap_func_excpbind_tab,
etab_line TYPE abap_func_excpbind,
data_export TYPE REF TO data,
data_ref TYPE REF TO data,
lv_response_json TYPE string. "接口返回参数
DATA: dyn_table TYPE REF TO data.
DATA: lt_dd04l TYPE STANDARD TABLE OF dd04l.
DATA:lv_error_msg TYPE bapi_msg.
"检查函数是否存在
SELECT funcname, paramtype, pposition, parameter, structure
FROM fupararef
WHERE funcname = @iv_function_name
INTO TABLE @DATA(parameters_tab).
IF sy-subrc <> 0.
response->set_status( code = 400 reason = 'target_function no exist,Please check the url address' ).
EXIT.
ENDIF.
LOOP AT parameters_tab ASSIGNING FIELD-SYMBOL(<ptab>).
IF strlen( <ptab>-structure ) > 30.
CONTINUE.
ENDIF.
lt_dd04l = VALUE #( BASE lt_dd04l ( domname = <ptab>-structure ) ).
ENDLOOP.
IF lt_dd04l IS NOT INITIAL.
SELECT domname FROM dd04l
FOR ALL ENTRIES IN @lt_dd04l
WHERE domname = @lt_dd04l-domname
INTO TABLE @DATA(data_elements).
ENDIF.
" MAPPING 字段映射
me->json_mapping( EXPORTING iv_function_name = iv_function_name
IMPORTING name_mappings_i = DATA(lt_mapping_i)
name_mappings_o = DATA(lt_mapping_o)
name_mappings_x = DATA(lt_mapping_x) ).
TRY.
"函数入参动态拼接
LOOP AT parameters_tab ASSIGNING FIELD-SYMBOL(<parameter>).
CLEAR ptab_line.
ptab_line-name = <parameter>-parameter.
ptab_line-kind = COND #( WHEN <parameter>-paramtype = 'E' THEN abap_func_importing
WHEN <parameter>-paramtype = 'I' THEN abap_func_exporting
WHEN <parameter>-paramtype = 'T' THEN abap_func_tables
WHEN <parameter>-paramtype = 'C' THEN abap_func_changing
ELSE ''
).
DATA(json_field_name) = COND string( WHEN ptab_line-kind = abap_func_exporting THEN 'IMPORT'
WHEN ptab_line-kind = abap_func_tables THEN 'TABLE'
WHEN ptab_line-kind = abap_func_changing THEN 'CHANGE'
WHEN ptab_line-kind = abap_func_importing THEN 'EXPORT'
ELSE ''
).
"解析传入参数
DATA(json) = request->get_cdata( ).
DATA(json_data) = zcl_json=>generate( json = json name_mappings = lt_mapping_i ).
ASSIGN json_data->* TO FIELD-SYMBOL(<json_data>).
READ TABLE lt_mapping_x INTO DATA(ls_x) WITH KEY abap = <parameter>-parameter.
IF sy-subrc EQ 0.
DATA(lv_parameter) = ls_x-json.
ELSE.
lv_parameter = <parameter>-parameter.
ENDIF.
"根据函数的入参匹配接口传入参数
ASSIGN COMPONENT lv_parameter OF STRUCTURE <json_data> TO FIELD-SYMBOL(<parameter_val>).
IF sy-subrc <> 0 OR json_field_name = 'EXPORT'.
CASE json_field_name.
WHEN 'TABLE'.
"创建动态表结构
CREATE DATA dyn_table TYPE TABLE OF (<parameter>-structure).
"创建动态内表
ASSIGN dyn_table->* TO FIELD-SYMBOL(<dyn_table>).
GET REFERENCE OF <dyn_table> INTO ptab_line-value.
INSERT ptab_line INTO TABLE ptab.
CONTINUE.
WHEN OTHERS.
"动态定义承接返回参数的结构
CREATE DATA data_export TYPE (<parameter>-structure).
ASSIGN data_export TO FIELD-SYMBOL(<data_export>).
ptab_line-value = <data_export>.
INSERT ptab_line INTO TABLE ptab.
CONTINUE.
ENDCASE.
ENDIF.
CREATE DATA data_ref TYPE (<parameter>-structure).
FIELD-SYMBOLS: <temp> TYPE any.
ASSIGN <parameter_val>->* TO <temp>.
IF data_ref IS BOUND.
ASSIGN data_ref->* TO FIELD-SYMBOL(<data_ref>).
ENDIF.
"将传入参数按照特定格式转换
IF line_exists( data_elements[ domname = <parameter>-structure ] ).
<data_ref> = <temp>.
ELSE.
DATA(json_temp) = /ui2/cl_json=>serialize( data = <parameter_val> ).
/ui2/cl_json=>deserialize( EXPORTING json = json_temp name_mappings = lt_mapping_i CHANGING data = <data_ref> ).
ENDIF.
GET REFERENCE OF <data_ref> INTO ptab_line-value.
INSERT ptab_line INTO TABLE ptab.
ENDLOOP.
CATCH cx_sy_create_data_error INTO DATA(lo_data_error).
lv_error_msg = lo_data_error->get_text( ).
me->set_message( EXPORTING request = request
iv_function_name = iv_function_name
iv_code = 400
iv_mtype = 'E'
iv_reason = 'target_function Handling Exceptions'
iv_message = lv_error_msg
IMPORTING response = response ).
EXIT.
ENDTRY.
etab_line-name = 'OTHERS'.
etab_line-value = 10.
INSERT etab_line INTO TABLE etab.
TRY.
"动态调用函数,业务逻辑部分
CALL FUNCTION iv_function_name
PARAMETER-TABLE
ptab
EXCEPTION-TABLE
etab.
SORT parameters_tab BY parameter.
CLEAR:data_ref.
UNASSIGN:<data_ref>.
LOOP AT ptab INTO ptab_line WHERE kind <> abap_func_exporting.
ASSIGN ptab_line-value->* TO <data_ref>.
"Serialize Data to Json
DATA(lv_string) = /ui2/cl_json=>serialize( data = <data_ref> name_mappings = lt_mapping_o ).
READ TABLE lt_mapping_x INTO ls_x WITH KEY abap = ptab_line-name.
IF sy-subrc EQ 0.
lv_parameter = ls_x-json.
ELSE.
lv_parameter = ptab_line-name.
ENDIF.
IF lv_response_json IS INITIAL.
lv_response_json = |" lv_parameter ": lv_string |.
ELSE.
lv_response_json = | lv_response_json ," lv_parameter ": lv_string |.
ENDIF.
ENDLOOP.
lv_response_json = '' && lv_response_json && ''.
CATCH cx_sy_dyn_call_param_not_found INTO DATA(lo_error_no_found).
lv_error_msg = lo_error_no_found->get_text( ).
CATCH cx_sy_dyn_call_param_missing INTO DATA(lo_error_missing).
lv_error_msg = lo_error_missing->get_text( ).
CATCH cx_sy_dyn_call_parameter_error INTO DATA(lo_error_parameter).
lv_error_msg = lo_error_parameter->get_text( ).
CATCH cx_sy_dyn_call_error INTO DATA(lo_error_call).
lv_error_msg = lo_error_call->get_text( ).
ENDTRY.
IF lv_error_msg IS NOT INITIAL.
me->set_message( EXPORTING request = request
iv_function_name = iv_function_name
iv_code = 400
iv_mtype = 'E'
iv_reason = 'target_function Handling Exceptions'
iv_message = lv_error_msg
IMPORTING response = response ).
EXIT.
ENDIF.
response->set_status( code = 200 reason = '' ).
response->set_header_field( name = 'Content-Type' value = 'application/json' ).
response->set_cdata( lv_response_json ).
ENDMETHOD.
PS:里面有一些表和mapping方法可以去掉,对代码本身没有影响。
我们通过
CALL FUNCTION iv_function_name
PARAMETER-TABLE
ptab
EXCEPTION-TABLE
etab.
可以动态的对函数进行调用。
不过sap标准的json转换方法存在一定的bug,/ui2/cl_json=>generate转换json的时候,如果是金额类型的字段,并且不加””,会导致json序列化转换的时候将字段转换成f类型,f类型会导致金额在超过千万的时候会自动转换成科学计数法,导致数据异常。我们在处理的时候将/ui2/cl_json这个类copy了出来并在GENERATE_INT方法里做了修改,将下图为之的so_type_f改成了so_type_s,数据转换成string类型就不会产生科学计数法问题了。(这个增强位置可能不同版本的系统不一样,需要找一下具体的位置)
同时在程序里还支持了动态传入的JSON字符和SAP系统字段不一样时的mapping,通过调用json_mapping对字段进行处理即可。
METHOD json_mapping.
SELECT fnam, " 函数名
direction, " 传输方向
field_sap, " sap字段名
field_other " 其他字段名
FROM zepict009
INTO TABLE @DATA(lt_mapping)
WHERE fnam = @iv_function_name.
CHECK lt_mapping IS NOT INITIAL.
SORT lt_mapping BY field_sap.
LOOP AT lt_mapping INTO DATA(ls_mapping).
CASE ls_mapping-direction. " 判断接口传入方向
WHEN 'I'.
INSERT VALUE #( abap = ls_mapping-field_sap
json = ls_mapping-field_other ) INTO TABLE name_mappings_i.
WHEN 'O'.
INSERT VALUE #( abap = ls_mapping-field_sap
json = ls_mapping-field_other ) INTO TABLE name_mappings_o.
WHEN 'X'.
INSERT VALUE #( abap = ls_mapping-field_sap
json = ls_mapping-field_other ) INTO TABLE name_mappings_x.
WHEN OTHERS.
INSERT VALUE #( abap = ls_mapping-field_sap
json = ls_mapping-field_other ) INTO TABLE name_mappings_i.
INSERT VALUE #( abap = ls_mapping-field_sap
json = ls_mapping-field_other ) INTO TABLE name_mappings_o.
ENDCASE.
ENDLOOP.
ENDMETHOD.
以上是关于sap restful json接口动态调用RFC,生成动态结构的主要内容,如果未能解决你的问题,请参考以下文章