* _______ _______ _______ _______ _______ _______ ______ _______ _______ ______ _______ * | _ | _ | _ | | | _ | || _ | _ | _ | | _ | * | |_| | |_| | |_| | _ | | |_| | _ | |_| | |_| | | || | |_| | * | | | | |_| | | | | | | | | |_||_| | * | | _ || | ___| _| | |_| | | _ || __ | | * | _ | |_| | _ | | | |_| _ | | _ | |_| | | | | _ | * |__| |__|_______|__| |__|___| |_______|__| |__|______||__| |__|_______|___| |_|__| |__| * www.abapcadabra.com *--------------------------------------------------------------------- * include : ZABAPCADABRA_EASY_XML * holding : Local LCL_EASY_XML class definition & implementation * title : Easy XML parsing (reading) through lcl_easy_xml * functional area : Cross modules * environment : 4.7 * program Function : This local class can be included in any ABAP report * and supports easy access to XML documents. The content * of XML documents can be checked (parsed) into ABAP * variables. * Documentation : Search for "Easy XML" on AbapcadabrA.com * There are simple "to-go" examples on XML parsing * such as attributes and repeating fields in simple * examples Abap reports. * Previous version : This is the initial version * Developer name : Wim Maasdam * Development date : 30/11/2015 * Version : 0.1 *--------------------------------------------------------------------- *--------------------------------------------------------------------- * C L A S S D E F I N I T I O N *--------------------------------------------------------------------- CLASS lcl_easy_xml DEFINITION. PUBLIC SECTION. TYPES: BEGIN OF ty_tagged_value, tag TYPE C length 50, val TYPE C length 150, END OF ty_tagged_value, BEGIN OF ty_node, tag_position TYPE string, element TYPE ty_tagged_value, attributes TYPE STANDARD TABLE OF ty_tagged_value WITH DEFAULT KEY, END OF ty_node, BEGIN OF ty_focus_area, tag_focus TYPE C length 50, index_from type sy-tabix, index_to type sy-tabix, END OF ty_focus_area. DATA: gt_xml TYPE STANDARD TABLE OF ty_node, gw_xml_node TYPE ty_node, gw_tagged_value TYPE ty_tagged_value, gv_result_found TYPE boolean, gv_gt_xml_index TYPE sy-tabix READ-only, gw_focus_area type ty_focus_area, gt_focus_areas type standard table of ty_focus_area. CLASS-METHODS: pretty_printer IMPORTING xml_string TYPE string EXPORTING pretty_xml TYPE TSTRINGS. METHODS: upload IMPORTING filename TYPE string, parse, findfirst IMPORTING tag TYPE ANY returning VALUE(val) TYPE string, findnext IMPORTING tag TYPE ANY DEFAULT space returning VALUE(val) TYPE string, attribute IMPORTING tag TYPE ANY returning VALUE(val) TYPE string, focus_set, focus_reset. PRIVATE SECTION. DATA: gv_tag_focus TYPE string, gt_xml_source TYPE TABLE OF string, gv_xml_source TYPE string, gv_filename TYPE string, gv_filesize TYPE I, gt_tagpath TYPE STANDARD TABLE OF string, gt_attributes TYPE STANDARD TABLE OF ty_tagged_value. METHODS: pop, push IMPORTING tag TYPE string, parse_attributes IMPORTING attribute_string TYPE string. ENDCLASS. *---------------------------------------------------------------------- * CLASS lcl_easy_xml IMPLEMENTATION *---------------------------------------------------------------------- CLASS lcl_easy_xml IMPLEMENTATION. METHOD upload. DATA: lv_xml TYPE string. gv_filename = filename. * Read the XML file: CALL FUNCTION 'GUI_UPLOAD' EXPORTING FILENAME = gv_filename FILETYPE = 'ASC' IMPORTING FILELENGTH = gv_filesize TABLES data_tab = gt_xml_source EXCEPTIONS OTHERS = 8. IF sy-subrc <> 0. MESSAGE 'Fout bij openen bestand'(g01) TYPE 'S'. EXIT. ENDIF. * The source is a table with strings, which should be converted to a * single string: CLEAR gv_xml_source. LOOP AT gt_xml_source INTO lv_xml. SHIFT lv_xml LEFT DELETING LEADING space. CONCATENATE gv_xml_source lv_xml INTO gv_xml_source. ENDLOOP. parse( ). ENDMETHOD. METHOD parse. TYPES: BEGIN OF ty_tagsets, tagpath TYPE string, tag TYPE string, attr TYPE string, val TYPE string, END OF ty_tagsets, BEGIN OF ty_fdpos, space TYPE sy-fdpos, greaterthan TYPE sy-fdpos, END OF ty_fdpos. DATA: lv_node TYPE ty_node, lv_string TYPE string, lt_tokens TYPE STANDARD TABLE OF string, lt_tagsets TYPE STANDARD TABLE OF ty_tagsets, lw_tagset TYPE ty_tagsets, lv_dummy TYPE string, lw_fdpos TYPE ty_fdpos, lv_len type sy-fdpos. CLEAR: gt_tagpath. * First parsing round: look for the tag openings: lv_string = gv_xml_source. SPLIT lv_string AT '<' INTO TABLE lt_tokens. LOOP AT lt_tokens INTO lv_string. CHECK NOT lv_string IS INITIAL. CHECK NOT lv_string(4) = '?xml'. * Empty tags are ignored or lv_len = strlen( lv_string ) - 2. check not ( not lv_string ca ' ' and lv_string+lv_len(2) = '/>' ). CLEAR lw_tagset. * Opening tag or closing tag ? IF lv_string(1) = '/'. pop( ). ELSE. * Are attributes available ? Test whether a space is available before the > CLEAR: lw_fdpos. IF lv_string CA ' '. lw_fdpos-space = sy-fdpos. ENDIF. IF lv_string CA '>'. lw_fdpos-greaterthan = sy-fdpos. ENDIF. IF lw_fdpos-greaterthan < lw_fdpos-space. SPLIT lv_string AT '>' INTO lw_tagset-tag lw_tagset-val. push( lw_tagset-tag ). ELSE. * Determine the tag name, which is up to the first space or > SPLIT lv_string AT space INTO lw_tagset-tag lw_tagset-attr. SPLIT lw_tagset-tag AT '>' INTO lw_tagset-tag lv_dummy. push( lw_tagset-tag ). SPLIT lw_tagset-attr AT '>' INTO lw_tagset-attr lw_tagset-val. ENDIF. * List all tags into the tag field; CLEAR lw_tagset-tagpath. LOOP AT gt_tagpath INTO lv_string. IF lw_tagset-tagpath IS INITIAL. lw_tagset-tagpath = lv_string. ELSE. CONCATENATE lw_tagset-tagpath lv_string INTO lw_tagset-tagpath SEPARATED BY '/'. ENDIF. ENDLOOP. IF NOT lw_tagset-attr IS INITIAL. * If the tag has a /> ending, it will need to be popped: lv_dummy = lw_tagset-attr. SHIFT lv_dummy RIGHT BY 1 PLACES CIRCULAR. IF lv_dummy(1) = '/'. pop( ). lw_tagset-attr = lv_dummy+1. "Remove the / ENDIF. ENDIF. APPEND lw_tagset TO lt_tagsets. ENDIF. ENDLOOP. CLEAR: gt_xml[], lv_node. * Transform the parse-results into the gt_xml setup. LOOP AT lt_tagsets INTO lw_tagset. lv_node-tag_position = lw_tagset-tagpath. lv_node-element-tag = lw_tagset-tag. lv_node-element-val = lw_tagset-val. replace ALL OCCURRENCES OF '&' in lv_node-element-val with '&'. replace ALL OCCURRENCES OF '<' in lv_node-element-val with '<'. replace ALL OCCURRENCES OF '>' in lv_node-element-val with '>'. parse_attributes( lw_tagset-attr ). lv_node-attributes[] = gt_attributes[]. APPEND lv_node TO gt_xml. ENDLOOP. ENDMETHOD. METHOD findfirst. * With the TAG that was passed on, find the first occurence. DATA: lw_xml_node TYPE ty_node, lv_tag_pattern TYPE C length 100, lv_min_tabix type sy-tabix, lv_max_tabix type sy-tabix. CLEAR: gv_result_found, gv_gt_xml_index. gv_tag_focus = tag. lv_min_tabix = 1. describe table gt_xml lines lv_max_tabix. if not GW_FOCUS_AREA is initial. lv_min_tabix = GW_FOCUS_AREA-index_from. lv_max_tabix = GW_FOCUS_AREA-index_to. endif. loop at gt_xml into gw_xml_node from lv_min_tabix to lv_max_tabix where tag_position = tag. endloop. IF sy-subrc = 0. * Exact match with full path gv_gt_xml_index = sy-tabix. val = gw_xml_node-element-val. gv_result_found = abap_true. ELSE. CONCATENATE '*' tag INTO lv_tag_pattern. LOOP AT gt_xml INTO lw_xml_node from lv_min_tabix to lv_max_tabix. IF lw_xml_node-tag_position CP lv_tag_pattern. gv_gt_xml_index = sy-tabix. gw_xml_node = lw_xml_node. val = gw_xml_node-element-val. gv_result_found = abap_true. EXIT. ENDIF. ENDLOOP. ENDIF. ENDMETHOD. METHOD findnext. * With the tag that was passed via findfirst, find the next occurence DATA: lw_xml_node TYPE ty_node, lv_tag_pattern TYPE C length 100, lv_lines TYPE I, lv_max_tabix type sy-tabix. CLEAR: gv_result_found. IF tag IS INITIAL. CHECK NOT gv_tag_focus IS INITIAL. ADD 1 TO gv_gt_xml_index. ELSE. IF gv_tag_focus <> tag. findfirst( tag ). ELSE. ADD 1 TO gv_gt_xml_index. ENDIF. ENDIF. DESCRIBE TABLE gt_xml LINES lv_lines. CHECK lv_lines >= gv_gt_xml_index. CONCATENATE '*' gv_tag_focus INTO lv_tag_pattern. * Determine how far the search can go: describe table gt_xml lines lv_max_tabix. if not GW_FOCUS_AREA is initial. lv_max_tabix = GW_FOCUS_AREA-index_to. endif. LOOP AT gt_xml INTO lw_xml_node FROM gv_gt_xml_index TO lv_max_tabix. IF lw_xml_node-tag_position CP lv_tag_pattern. gv_gt_xml_index = sy-tabix. gw_xml_node = lw_xml_node. val = gw_xml_node-element-val. gv_result_found = abap_true. EXIT. ENDIF. ENDLOOP. ENDMETHOD. METHOD attribute. * For the node on gw_xml_node, search the attributes and return the value CLEAR: gv_result_found, val. READ TABLE gw_xml_node-attributes INTO gw_tagged_value WITH KEY tag = tag. IF sy-subrc = 0. gv_result_found = abap_true. val = gw_tagged_value-val. replace ALL OCCURRENCES OF '&' in val with '&'. replace ALL OCCURRENCES OF '<' in val with '<'. replace ALL OCCURRENCES OF '>' in val with '>'. ENDIF. ENDMETHOD. METHOD focus_set. data: lw_xml_node TYPE ty_node, lv_tabix type sy-tabix, lv_tag_pattern TYPE C length 100, lv_lines TYPE I. clear gw_focus_area. gw_focus_area-tag_focus = gv_tag_focus. gw_focus_area-index_from = gv_gt_xml_index. gw_focus_area-index_to = gv_gt_xml_index. lv_tabix = gw_focus_area-index_from + 1. describe table gt_xml lines lv_lines. check lv_lines > lv_tabix. CONCATENATE gw_focus_area-tag_focus '*' INTO lv_tag_pattern. * Determine the end of the range: loop at gt_xml INTO lw_xml_node FROM lv_tabix. if lw_xml_node-tag_position = gw_focus_area-tag_focus or not lw_xml_node-tag_position cp lv_tag_pattern. exit. endif. add 1 to gw_focus_area-index_to. endloop. append gw_focus_area to gt_focus_areas. ENDMETHOD. METHOD focus_reset. data: lv_lines type i. describe table gt_focus_areas lines lv_lines. read table gt_focus_areas into gw_focus_area index lv_lines. GV_TAG_FOCUS = gw_focus_area-tag_focus. gv_gt_xml_index = gw_focus_area-index_from. delete gt_focus_areas from lv_lines. clear: gw_focus_area. describe table gt_focus_areas lines lv_lines. read table gt_focus_areas into gw_focus_area index lv_lines. ENDMETHOD. METHOD pop. DATA: lv_tabix TYPE sy-tabix. DESCRIBE TABLE gt_tagpath LINES lv_tabix. DELETE gt_tagpath INDEX lv_tabix. ENDMETHOD. METHOD push. APPEND tag TO gt_tagpath. ENDMETHOD. METHOD parse_attributes. DATA: lv_source TYPE string, lv_fieldname TYPE string, lv_fieldvalue TYPE string, lv_dummy TYPE string, lw_tagged_value TYPE ty_tagged_value. CLEAR: gt_attributes[]. CHECK NOT attribute_string IS INITIAL. lv_source = attribute_string. WHILE NOT lv_source CO space. SHIFT lv_source LEFT DELETING LEADING space. SPLIT lv_source AT '=' INTO lv_fieldname lv_fieldvalue. IF lv_fieldname IS INITIAL. CLEAR lv_source. CONTINUE. ENDIF. SPLIT lv_fieldvalue AT '"' INTO lv_dummy lv_fieldvalue lv_source. lw_tagged_value-tag = lv_fieldname. lw_tagged_value-val = lv_fieldvalue. APPEND lw_tagged_value TO gt_attributes. ENDWHILE. ENDMETHOD. METHOD pretty_printer. DATA: l_cnt_length TYPE I, l_cnt_index TYPE I, l_xmlline TYPE string, l_oldchar TYPE C, l_char TYPE C, l_nextchar TYPE C, l_indent TYPE I. l_cnt_length = STRLEN( xml_string ). DO l_cnt_length TIMES. l_cnt_index = sy-INDEX - 1. l_char = xml_string+l_cnt_index(1). IF sy-INDEX < l_cnt_length. l_nextchar = xml_string+sy-INDEX(1). ENDIF. IF l_char <> cl_abap_char_utilities=>newline. IF ( l_char = '<' AND l_nextchar = '/' ) OR ( l_char = '/' AND l_nextchar = '>' ). l_indent = l_indent - 1. IF l_indent < 0. l_indent = 0. ENDIF. ENDIF. IF l_char = '<' AND l_oldchar = '>'. APPEND l_xmlline TO pretty_xml. * write: / l_xmlline. CLEAR l_xmlline. DO l_indent TIMES. CONCATENATE space space l_xmlline INTO l_xmlline SEPARATED BY space. ENDDO. ENDIF. IF l_char <> space. CONCATENATE l_xmlline l_char INTO l_xmlline. ELSE. CONCATENATE l_xmlline l_char INTO l_xmlline SEPARATED BY space. ENDIF. IF l_char = '<' AND l_nextchar <> '/'. l_indent = l_indent + 1. ENDIF. ELSE. l_indent = 0. APPEND l_xmlline TO pretty_xml. CLEAR l_xmlline. ENDIF. l_oldchar = l_char. ENDDO. APPEND l_xmlline TO pretty_xml. ENDMETHOD. ENDCLASS. * _______ _______ _______ _______ _______ _______ ______ _______ _______ ______ _______ * | _ | _ | _ | | | _ | || _ | _ | _ | | _ | * | |_| | |_| | |_| | _ | | |_| | _ | |_| | |_| | | || | |_| | * | | | | |_| | | | | | | | | |_||_| | * | | _ || | ___| _| | |_| | | _ || __ | | * | _ | |_| | _ | | | |_| _ | | _ | |_| | | | | _ | * |__| |__|_______|__| |__|___| |_______|__| |__|______||__| |__|_______|___| |_|__| |__| * www.abapcadabra.com