The following ABAP function module source code is for a generic HTTP client. It allows you to send XML messages in any format via HTTP/HTTPS, with just a few mapping table entries. Blue Harbors uses this client with Express Ship Cloud Solution.
In addition to creating the function module, you’ll need to define two tables. The table definitions are provided after the code.
You can pass it any data structure. You just need to maintain XML mapping entries in the tables provided. In addition if you include a single character field SHOW_XML in your structure, it displays the XML request and response messages, which is great for testing.
[cc lang=”abap” tab_size=”2″ lines=”15″]
function Z_BH_HTTP_CLIENT .
*”———————————————————————-
*”*”Local Interface:
*” IMPORTING
*” REFERENCE(MESSAGE) TYPE ZBH_XML_MESSAGE
*” REFERENCE(DATA)
*” EXPORTING
*” REFERENCE(RESPONSE)
*” EXCEPTIONS
*” MESSAGE_HEADER_NOT_FOUND
*” MESSAGE_MAPPING_NOT_FOUND
*” FIELD_NOT_FOUND
*” ROOT_NODE_NOT_FOUND
*” INVALID_MAPPING
*” ERROR_DISPLAYING_XML
*” COMMUNICATION_ERROR
*”———————————————————————-
data: l_ixml type ref to if_ixml,
l_xml_document type ref to if_ixml_document,
l_xml_node type ref to if_ixml_element,
l_xml_doc type ref to cl_xml_document_base,
l_client type ref to if_http_client,
l_xstring type xstring,
l_name type string,
l_value type string,
l_root type string,
l_xml type string,
l_url type string,
l_uid type string,
l_pwd type string,
l_type,
lt_xml_map type zbh_xml_map occurs 0 with header line,
l_xml_map type zbh_xml_map,
l_field type string,
l_xml_hdr type zbh_xml_hdr.
data: begin of lt_parent occurs 0,
map_index type zbh_xml_map-map_index,
xml_parent type ref to if_ixml_element,
end of lt_parent.
field-symbols: <fs>, <fs_table> type standard table.
* get mapping for message
select single * into l_xml_hdr
from zbh_xml_hdr
where message eq message.
if sy-subrc ne 0.
message e000(zbh) with ‘XML header for’ message ‘not found’
raising message_header_not_found.
endif.
select * into table lt_xml_map
from zbh_xml_map
where message eq message
order by map_index sub_index.
if sy-subrc ne 0.
message e000(zbh) with ‘XML mapping for’ message ‘not found’
raising message_mapping_not_found.
endif.
* create XML doc based on mapping
l_ixml = cl_ixml=>create( ).
l_xml_document = l_ixml->create_document( ).
* loop thru mapping rules
loop at lt_xml_map where response_flag eq space.
l_xml_map = lt_xml_map.
l_name = lt_xml_map-node_name.
at new map_index.
clear l_value.
endat.
* parent node
if lt_xml_map-source_field eq space and lt_xml_map-sub_index eq 0.
* root node
if lt_xml_map-parent_index eq 0.
lt_parent-xml_parent = l_xml_document->create_element( name = l_name ).
l_root = l_name.
else.
read table lt_parent with key map_index = lt_xml_map-parent_index.
lt_parent-xml_parent = l_xml_document->create_simple_element( name = l_name parent = lt_parent-xml_parent value = space ).
endif.
lt_parent-map_index = lt_xml_map-map_index.
append lt_parent.
* regular child node
elseif lt_xml_map-sub_index eq 0.
concatenate ‘DATA-‘ lt_xml_map-source_field into l_field.
assign (l_field) to <fs>.
* constant
if sy-subrc ne 0.
l_value = lt_xml_map-source_field.
else.
l_value = <fs>.
endif.
read table lt_parent with key map_index = lt_xml_map-parent_index.
l_xml_node = l_xml_document->create_simple_element( name = l_name parent = lt_parent-xml_parent value = l_value ).
* special child node
else.
concatenate ‘DATA-‘ lt_xml_map-source_field into l_field.
assign (l_field) to <fs>.
* constant
if sy-subrc ne 0.
concatenate l_value lt_xml_map-field_id ‘,”‘ lt_xml_map-source_field ‘”‘ into l_value.
else.
concatenate l_value lt_xml_map-field_id ‘,”‘ <fs> ‘”‘ into l_value.
endif.
endif.
at end of map_index.
if l_xml_map-sub_index ne 0.
l_xml_node = l_xml_document->create_simple_element( name = l_name parent = lt_parent-xml_parent value = l_value ).
endif.
endat.
endloop.
* add root node ot XML doc
read table lt_parent index 1.
if sy-subrc eq 0.
l_xml_document->append_child( new_child = lt_parent-xml_parent ).
else.
l_xml_document->append_child( new_child = l_xml_node ).
endif.
* show XML request?
assign (‘DATA-SHOW_XML’) to <fs>.
if sy-subrc eq 0 and <fs> eq ‘X’.
create object l_xml_doc
exporting
document = l_xml_document.
call function ‘DISPLAY_XML_DOCUMENT’
exporting
xml_document = l_xml_doc
title = ‘XML message’
starting_x = 20
starting_y = 2
exceptions
no_xml_document = 1
others = 2.
if sy-subrc <> 0.
message id sy-msgid type sy-msgty number sy-msgno
with sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4
raising error_displaying_xml.
endif.
endif.
* convert to string
call function ‘SDIXML_DOM_TO_XML’
exporting
document = l_xml_document
importing
xml_as_string = l_xstring
exceptions
no_document = 1
others = 2.
if sy-subrc <> 0.
message id sy-msgid type sy-msgty number sy-msgno
with sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
endif.
l_xml = cl_proxy_service=>xstring2cstring( l_xstring ).
* add root node attribute (not supported by XML element)
if l_xml_hdr-root_node_attrib ne space.
concatenate l_root l_xml_hdr-root_node_attrib into l_name separated by space.
concatenate l_name ‘=”‘ l_xml_hdr-attribute_value ‘”‘ into l_name.
replace first occurrence of l_root in l_xml with l_name.
endif.
* create HTTP client
l_url = l_xml_hdr-url.
call method cl_http_client=>create_by_url
exporting
url = l_url
* ssl_id = ‘DFAULT’
importing
client = l_client
exceptions
argument_not_found = 1
plugin_not_active = 2
internal_error = 3
others = 4.
if sy-subrc ne 0.
message e000(zbh) with ‘Error creating HTTP client’
raising communication_error.
endif.
* build request data
concatenate l_xml_hdr-request l_xml into l_xml.
call method l_client->request->set_cdata
exporting
data = l_xml.
if l_xml_hdr-userid ne space and l_xml_hdr-password ne space.
l_uid = l_xml_hdr-userid.
l_pwd = l_xml_hdr-password.
call method l_client->authenticate
exporting
username = l_uid
password = l_pwd.
endif.
call method l_client->request->set_header_field
exporting
name = ‘Content-Type’
value = ‘text/xml’.
* send request
call method l_client->send
exceptions
http_communication_failure = 1
http_invalid_state = 2.
if sy-subrc ne 0.
message e000(zbh) with ‘Error sending message’
raising communication_error.
endif.
call method l_client->receive
exceptions
http_communication_failure = 1
http_invalid_state = 2
http_processing_failed = 3.
if sy-subrc ne 0.
message e000(zbh) with ‘Error receiving response’
raising communication_error.
endif.
* process response as XML doc
l_xstring = l_client->response->get_data( ).
call function ‘SDIXML_XML_TO_DOM’
exporting
xml = l_xstring
importing
document = l_xml_document
exceptions
invalid_input = 1
others = 2.
if sy-subrc <> 0.
message e000(zbh) with ‘Error converting response’
raising communication_error.
endif.
* check for BC value tag
clear l_xml_node.
l_xml_node = l_xml_document->find_from_name( name = ‘value’ ).
if l_xml_node is not initial.
l_value = l_xml_node->get_value( ).
l_xstring = cl_proxy_service=>cstring2xstring( l_value ).
call function ‘SDIXML_XML_TO_DOM’
exporting
xml = l_xstring
importing
document = l_xml_document
exceptions
invalid_input = 1
others = 2.
if sy-subrc <> 0.
message e000(zbh) with ‘Error converting response’
raising communication_error.
endif.
endif.
* show XML response?
assign (‘DATA-SHOW_XML’) to <fs>.
if sy-subrc eq 0 and <fs> eq ‘X’.
create object l_xml_doc
exporting
document = l_xml_document.
call function ‘DISPLAY_XML_DOCUMENT’
exporting
xml_document = l_xml_doc
title = ‘XML message’
starting_x = 20
starting_y = 2
exceptions
no_xml_document = 1
others = 2.
if sy-subrc <> 0.
message id sy-msgid type sy-msgty number sy-msgno
with sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4
raising error_displaying_xml.
endif.
endif.
* loop thru mapping rules for response
loop at lt_xml_map where response_flag eq ‘X’.
l_name = lt_xml_map-node_name.
clear l_xml_node.
l_xml_node = l_xml_document->find_from_name( name = l_name ).
if l_xml_node is not initial.
concatenate ‘RESPONSE-‘ lt_xml_map-source_field into l_field.
assign (l_field) to <fs>.
if sy-subrc ne 0.
message e000(zbh) with ‘Field’ lt_xml_map-source_field ‘does not exist’
raising field_not_found.
else.
describe field <fs> type l_type.
if l_type eq ‘h’. “internal table
assign (l_field) to <fs_table>.
endif.
l_value = l_xml_node->get_value( ).
if l_value is not initial.
if lt_xml_map-convert_flag eq ‘X’.
call function ‘SSFC_BASE64_DECODE’
exporting
b64data = l_value
importing
bindata = l_xstring
exceptions
ssf_krn_error = 1
ssf_krn_noop = 2
ssf_krn_nomemory = 3
ssf_krn_opinv = 4
ssf_krn_input_data_error = 5
ssf_krn_invalid_par = 6
ssf_krn_invalid_parlen = 7
others = 8.
if sy-subrc <> 0.
message e000(zbh) with ‘Error converting response’
raising communication_error.
endif.
if l_type eq ‘h’. “internal table
append l_xstring to <fs_table>.
else.
<fs> = l_xstring.
endif.
else.
if l_type eq ‘h’. “internal table
append l_value to <fs_table>.
else.
<fs> = l_value.
endif.
endif.
endif.
endif.
endif.
endloop.
endfunction.
[/cc]
The following tables need to be created. Rather than replicating the data elements, you can use predefined types:
XML Header Table – ZBH_XML_HDR
MESSAGE | ZBH_XML_MESSAGE | CHAR | 10 | 0 | XML Message ID |
URL | ZBH_XML_URL | CHAR | 255 | 0 | URL |
ROOT_NODE_ATTRIB | ZBH_XML_ELEMENT | CHAR | 30 | 0 | XML element name |
ATTRIBUTE_VALUE | ZBH_XML_VALUE | CHAR | 30 | 0 | XML value |
REQUEST | ZBH_XML_REQUEST | CHAR | 255 | 0 | XML request string |
USERID | ZBH_XML_USER | CHAR | 30 | 0 | XML user ID |
PASSWORD | ZBH_XML_PASSWORD | CHAR | 30 | 0 | XML password |
XML Mapping Table – ZBH_XML_MAP
MESSAGE | ZBH_XML_MESSAGE | CHAR | 10 | 0 | XML Message ID |
MAP_INDEX | ZBH_XML_MAP_INDEX | NUMC | 3 | 0 | XML mapping index |
SUB_INDEX | ZBH_XML_SUB_INDEX | NUMC | 3 | 0 | XML mapping sub-index |
PARENT_INDEX | ZBH_XML_PARENT_INDEX | NUMC | 3 | 0 | XML parent index |
NODE_NAME | ZBH_XML_ELEMENT | CHAR | 30 | 0 | XML element name |
FIELD_ID | ZBH_XML_FIELD_ID | CHAR | 10 | 0 | Field identifier (optional) |
SOURCE_FIELD | ZBH_XML_VALUE | CHAR | 30 | 0 | Source field or literal value |
RESPONSE_FLAG | ZBH_XML_RESPONSE | CHAR | 1 | 0 | XML response mapping |
CONVERT_FLAG | ZBH_XML_CONVERT | CHAR | 1 | 0 | Convert base64 |
Sample table entries are provided below for sending a ship request to USPS.
XML Header Table – ZBH_XML_HDR
MESSAGE | USPS_PCKUP |
URL | https://secure.shippingapis.com/ShippingAPITest.dll |
ROOT_NODE_ATTRIB | USERID |
ATTRIBUTE_VALUE | <insert USPS user ID here> |
REQUEST | API=CarrierPickupSchedule&XML= |
USERID | |
PASSWORD |
XML Mapping Table – ZBH_XML_MAP
USPS_PCKUP | 10 | CarrierPickupScheduleRequest | |||||
USPS_PCKUP | 20 | 10 | FirstName | NAME1_FR | |||
USPS_PCKUP | 30 | 10 | LastName | NAME2_FR | |||
USPS_PCKUP | 40 | 10 | FirmName | ||||
USPS_PCKUP | 50 | 10 | SuiteOrApt | STR_SUPPL1_FR | |||
USPS_PCKUP | 60 | 10 | Address2 | STRAS_FR | |||
USPS_PCKUP | 70 | 10 | Urbanization | ||||
USPS_PCKUP | 80 | 10 | City | ORT01_FR | |||
USPS_PCKUP | 90 | 10 | State | REGIO_FR | |||
USPS_PCKUP | 100 | 10 | ZIP5 | PSTLZ_FR | |||
USPS_PCKUP | 110 | 10 | ZIP4 | ||||
USPS_PCKUP | 120 | 10 | Phone | TELF1_FR | |||
USPS_PCKUP | 130 | 10 | Extension | ||||
USPS_PCKUP | 140 | 10 | Package | ||||
USPS_PCKUP | 150 | 140 | ServiceType | ExpressMail | |||
USPS_PCKUP | 160 | 140 | Count | 1 | |||
USPS_PCKUP | 170 | 10 | Package | ||||
USPS_PCKUP | 180 | 170 | ServiceType | PriorityMail | |||
USPS_PCKUP | 190 | 170 | Count | 1 | |||
USPS_PCKUP | 200 | 10 | Package | ||||
USPS_PCKUP | 210 | 200 | ServiceType | International | |||
USPS_PCKUP | 220 | 200 | Count | 1 | |||
USPS_PCKUP | 230 | 10 | Package | ||||
USPS_PCKUP | 240 | 230 | ServiceType | OtherPackages | |||
USPS_PCKUP | 250 | 230 | Count | 1 | |||
USPS_PCKUP | 260 | 10 | EstimatedWeight | WEIGHT | |||
USPS_PCKUP | 270 | 10 | PackageLocation | Other | |||
USPS_PCKUP | 280 | 10 | SpecialInstructions | Beware of dog | |||
USPS_PCKUP | 510 | Description | MESSAGES | X | |||
USPS_PCKUP | 520 | ConfirmationNumber | TRACKING_NO | X |