* _______ _______ _______ _______ _______ _______ ______ _______ _______ ______ _______ * | _ | _ | _ | | | _ | || _ | _ | _ | | _ | * | |_| | |_| | |_| | _ | | |_| | _ | |_| | |_| | | || | |_| | * | | | | |_| | | | | | | | | |_||_| | * | | _ || | ___| _| | |_| | | _ || __ | | * | _ | |_| | _ | | | |_| _ | | _ | |_| | | | | _ | * |__| |__|_______|__| |__|___| |_______|__| |__|______||__| |__|_______|___| |_|__| |__| * www.abapcadabra.com * *------------------------------------------------------------------------------------------- * program : ZABAPCADABRA_XML_PARSER * title : Simple XML parser * functional area : Cross modules * environment : 4.7 * program Function : If you are trying to interpret the content of an XML file, * and iXML and CL_XML_DOCUMENT are too cumbersome to use, * this lcl_easy_xml class may just be your answer. Example * report that reads an XML and makes it available to you ! * Documentation : Search for "Simple XML parsing" on AbapcadabrA.com * Previous version : This is the initial version * Developer name : Wim Maasdam * Development date : 09/10/2015 * Version : 0.1 *--------------------------------------------------------------------- * Change list: * Date Description * 09/10/2015 Initial release *--------------------------------------------------------------------- REPORT ZABAPCADABRA_XML_PARSER. CLASS lcl_xml_node DEFINITION. PUBLIC SECTION. types: begin of ty_tagged_value, tag type c length 50, val type c length 150, end of ty_tagged_value, ty_tagged_values type STANDARD TABLE OF ty_tagged_value. data: gw_element type ty_tagged_value, gt_attributes type ty_tagged_values. ENDCLASS. CLASS lcl_easy_xml DEFINITION. PUBLIC SECTION. types: begin of ty_node, tag_position type string, node type ref to lcl_xml_node, end of ty_node. data: gt_xml TYPE STANDARD TABLE OF ty_node, gw_xml_node type ty_node, gw_tagged_value type lcl_xml_node=>ty_tagged_value, gv_result_found type boolean, gv_gt_xml_index type sy-tabix read-only. 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. 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 lcl_xml_node=>ty_tagged_values. methods: pop, push importing tag type string, parse_attributes importing attribute_string type string. ENDCLASS. 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 'Error opening file' 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. 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'. 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. CREATE object lv_node-node. lv_node-node->gw_element-tag = lw_tagset-tag. lv_node-node->gw_element-val = lw_tagset-val. parse_attributes( lw_tagset-attr ). lv_node-node->gt_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. clear: gv_result_found, gv_gt_xml_index. gv_tag_focus = tag. read table gt_xml with key tag_position = tag into gw_xml_node. if sy-subrc = 0. * Exact match with full path gv_gt_xml_index = sy-tabix. val = gw_xml_node-node->gw_element-val. gv_result_found = abap_true. else. concatenate '*' tag into lv_tag_pattern. loop at gt_xml into lw_xml_node. 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-node->gw_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. 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. loop at gt_xml into lw_xml_node from gv_gt_xml_index. 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-node->gw_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-node->gt_attributes into gw_tagged_value with key tag = tag. if sy-subrc = 0. gv_result_found = abap_true. val = gw_tagged_value-val. endif. 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 lcl_xml_node=>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. ENDCLASS. *--------------------------------------------------------------------- * S E L E C T I O N - S C R E E N *--------------------------------------------------------------------- selection-SCREEN BEGIN OF LINE. selection-SCREEN COMMENT 1(23) lbl_01 FOR FIELD pa_filen. PARAMETERS: pa_filen TYPE string LOWER CASE OBLIGATORY. selection-SCREEN END OF LINE. INITIALIZATION. lbl_01 = 'Local XML file'. START-OF-SELECTION. * Define a class variabele for lvl_easy_xml: data: go_myxml type ref to lcl_easy_xml. create object go_myxml. go_myxml->upload( pa_filen ). * The rest of the coding is do display the XML output from an Abap internal format: format reset. * Run through the XML structure: loop at go_myxml->gt_xml into go_myxml->gw_xml_node. format color 5. write: / 'Node:' color 3, (50) go_myxml->gw_xml_node-tag_position, go_myxml->gw_xml_node-node->gw_element-val(60). format color 4. loop at go_myxml->gw_xml_node-node->gt_attributes into go_myxml->gw_tagged_value. write: / 'Attr:' color 3, (50) go_myxml->gw_tagged_value-tag, go_myxml->gw_tagged_value-val(60). endloop. format reset. endloop. * Demonstration of the use of findfirst, findnext and attribute: write: /(30) 'The value of Title is', go_myxml->findfirst( 'title' ). write: /(30) 'More specifically', go_myxml->findfirst( 'report/attributes/title' ). write: /(30) 'The first line:', go_myxml->findfirst( 'report/abapcoding/line' ). while go_myxml->gv_result_found = abap_true. write: /(30) '', go_myxml->findnext( ). endwhile. * Focus the go_myxml->gw_xml_node attribute: go_myxml->findfirst( 'report/attributes/author' ). if go_myxml->gv_result_found = abap_true. write: / 'Author user ID is', go_myxml->attribute( 'userid' ). write: / 'And the author''s name:', go_myxml->gw_xml_node-node->gw_element-val. endif. do. write: /(30) '', go_myxml->findnext( 'report/abapcoding/line' ). if go_myxml->gv_result_found = abap_false. exit. endif. enddo.