-
ABAP — HTML/XML 속성값 추출 패턴SAP/소스 2026. 5. 27. 18:54
ABAP — HTML/XML 속성값 추출 패턴 ABAP — HTML/XML 속성값 추출 패턴
📋 목차1. 방법 비교 — 언제 무엇을 쓸까?
방법 적합한 상황 장점 단점 REGEX FIND … REGEXHTML (비정형), 특정 속성 1~2개만 필요 코드 단순, 빠름 중첩 구조 취약, 복잡한 HTML 오파싱 가능 cl_ixml Well-formed XML / XHTML, 전체 속성 탐색 표준 DOM 지원, 구조적 탐색 가능 코드량 많음, HTML은 파싱 실패 가능 문자열 파싱 FIND / SPLIT구조가 단순·고정된 텍스트 외부 의존 없음, 이해 쉬움 패턴 변형에 취약 💡 선택 기준: SAP PI/PO 메시지·IDoc XML →
cl_ixml/ 웹 스크래핑·HTML 파편 →REGEX/ 단순 고정 포맷 텍스트 → 문자열 파싱2. REGEX FIND 문 속성값 추출
FIND … REGEX … SUBMATCHES를 사용해 캡처 그룹으로 속성값을 뽑는다. HTML처럼 정형화되지 않은 텍스트에 가장 빠르게 적용할 수 있다.2-1. 단일 속성 추출
" 대상: <input type="text" id="username" value="홍길동" /> DATA: lv_html TYPE string, lv_value TYPE string. lv_html = '<input type="text" id="username" value="홍길동" />'. " value 속성값 추출 " 패턴: value\s*=\s*["']([^"']*)["'] FIND REGEX 'value\s*=\s*["\x27]([^"\x27]*)["\x27]' IN lv_html SUBMATCHES lv_value. IF sy-subrc = 0. WRITE: / |value = { lv_value }|. " → 홍길동 ENDIF.💡
\x27은 작은따옴표(') ASCII 16진수 표현이다. ABAP 문자열 리터럴 안에서'를 직접 쓰면 문자열이 닫히므로\x27로 대체한다.2-2. 여러 속성 한 번에 추출 (SUBMATCHES 다중)
" 대상: <a href="https://example.com" class="link" target="_blank">링크</a> DATA: lv_src TYPE string, lv_href TYPE string, lv_class TYPE string, lv_target TYPE string. lv_src = '<a href="https://example.com" class="link" target="_blank">링크</a>'. " href 추출 FIND REGEX 'href\s*=\s*["\x27]([^"\x27]*)["\x27]' IN lv_src SUBMATCHES lv_href. " class 추출 FIND REGEX 'class\s*=\s*["\x27]([^"\x27]*)["\x27]' IN lv_src SUBMATCHES lv_class. " target 추출 FIND REGEX 'target\s*=\s*["\x27]([^"\x27]*)["\x27]' IN lv_src SUBMATCHES lv_target. WRITE: / |href = { lv_href }|. " → https://example.com WRITE: / |class = { lv_class }|. " → link WRITE: / |target = { lv_target }|. " → _blank2-3. 반복 태그에서 전체 속성값 수집 (FIND ALL OCCURRENCES)
" 대상: 여러 <img src="..."> 태그에서 src 전체 추출 DATA: lv_html TYPE string, lt_match TYPE match_result_tab, ls_match TYPE match_result, lv_src TYPE string. lv_html = '<img src="logo.png" alt="로고"/>' && '<img src="banner.jpg" alt="배너"/>' && '<img src="icon.svg" alt="아이콘"/>'. " 모든 src 값 수집 FIND ALL OCCURRENCES OF REGEX 'src\s*=\s*["\x27]([^"\x27]*)["\x27]' IN lv_html RESULTS lt_match. LOOP AT lt_match INTO ls_match. " 첫 번째 캡처 그룹 (submatches 첫 요소) lv_src = lv_html+ls_match-submatches[ 1 ]-offset( ls_match-submatches[ 1 ]-length ). WRITE: / |src = { lv_src }|. ENDLOOP. " → logo.png / banner.jpg / icon.svg2-4. cl_abap_regex / cl_abap_matcher (OOP 방식)
DATA: lo_regex TYPE REF TO cl_abap_regex, lo_matcher TYPE REF TO cl_abap_matcher, lv_html TYPE string, lv_result TYPE string. lv_html = '<div data-id="42" data-name="테스트" class="box"></div>'. " data-id 추출 lo_regex = cl_abap_regex=>create( pattern = 'data-id\s*=\s*["\x27]([^"\x27]*)["\x27]' ignore_case = abap_true ). lo_matcher = lo_regex->create_matcher( text = lv_html ). IF lo_matcher->find_next( ) = abap_true. lv_result = lo_matcher->get_submatch( 1 ). WRITE: / |data-id = { lv_result }|. " → 42 ENDIF.3. cl_ixml SAP 표준 XML DOM 파서
cl_ixml은 SAP 표준 XML 파서로, Well-formed XML 문서를 DOM 트리로 파싱한다. PI/PO 메시지, IDoc XML, SAP 내부 XML 처리에 권장한다.3-1. 기본 구조 — 파서 생성 → 파싱 → DOM 탐색
DATA: lv_xml TYPE string, lo_ixml TYPE REF TO if_ixml, lo_sfactory TYPE REF TO if_ixml_stream_factory, lo_istream TYPE REF TO if_ixml_istream, lo_document TYPE REF TO if_ixml_document, lo_parser TYPE REF TO if_ixml_parser, lv_xml_raw TYPE xstring, lv_rc TYPE i. lv_xml = '<?xml version="1.0" encoding="UTF-8"?>' && '<orders>' && ' <order id="1001" status="ACTIVE" amount="99.50">' && ' <customer cid="C01" name="홍길동"/>' && ' </order>' && ' <order id="1002" status="CLOSED" amount="150.00">' && ' <customer cid="C02" name="김철수"/>' && ' </order>' && '</orders>'. " ── 1. xstring 변환 ─────────────────────────────────────────────── CALL FUNCTION 'SCMS_STRING_TO_XSTRING' EXPORTING text = lv_xml mimetype = 'text/xml' encoding = 'UTF-8' IMPORTING buffer = lv_xml_raw EXCEPTIONS OTHERS = 1. " ── 2. 파서 생성 ────────────────────────────────────────────────── lo_ixml = cl_ixml=>create( ). lo_sfactory = lo_ixml->create_stream_factory( ). lo_istream = lo_sfactory->create_istream_xstring( string = lv_xml_raw ). lo_document = lo_ixml->create_document( ). lo_parser = lo_ixml->create_parser( stream_factory = lo_sfactory istream = lo_istream document = lo_document ). " ── 3. 파싱 실행 ────────────────────────────────────────────────── lv_rc = lo_parser->parse( ). IF lv_rc <> 0. WRITE: / '파싱 오류 발생'. RETURN. ENDIF.3-2. getElementsByTagName — 태그별 속성 추출
DATA: lo_elements TYPE REF TO if_ixml_node_list, lo_element TYPE REF TO if_ixml_element, lo_node TYPE REF TO if_ixml_node, lv_id TYPE string, lv_status TYPE string, lv_amount TYPE string, lv_idx TYPE i. " <order> 태그 전체 수집 lo_elements = lo_document->get_elements_by_tag_name( 'order' ). lv_idx = 0. WHILE lv_idx < lo_elements->get_length( ). lo_node = lo_elements->get_item( lv_idx ). lo_element ?= lo_node. " 속성값 추출: get_attribute( ) lv_id = lo_element->get_attribute( 'id' ). lv_status = lo_element->get_attribute( 'status' ). lv_amount = lo_element->get_attribute( 'amount' ). WRITE: / |Order id={ lv_id } status={ lv_status } amount={ lv_amount }|. lv_idx = lv_idx + 1. ENDWHILE. " → Order id=1001 status=ACTIVE amount=99.50 " → Order id=1002 status=CLOSED amount=150.003-3. 중첩 자식 노드 속성 추출
DATA: lo_child TYPE REF TO if_ixml_node, lo_cust_el TYPE REF TO if_ixml_element, lv_cid TYPE string, lv_cname TYPE string. lv_idx = 0. WHILE lv_idx < lo_elements->get_length( ). lo_node = lo_elements->get_item( lv_idx ). lo_element ?= lo_node. lv_id = lo_element->get_attribute( 'id' ). " 자식 노드 순회 → <customer> 찾기 lo_child = lo_element->get_first_child( ). WHILE lo_child IS BOUND. IF lo_child->get_type( ) = if_ixml_node=>co_node_element. lo_cust_el ?= lo_child. IF lo_cust_el->get_name( ) = 'customer'. lv_cid = lo_cust_el->get_attribute( 'cid' ). lv_cname = lo_cust_el->get_attribute( 'name' ). WRITE: / | Order { lv_id } → 고객 { lv_cid }: { lv_cname }|. ENDIF. ENDIF. lo_child = lo_child->get_next( ). ENDWHILE. lv_idx = lv_idx + 1. ENDWHILE.3-4. 전체 속성 열거 (속성명 모를 때)
DATA: lo_attrs TYPE REF TO if_ixml_named_node_map, lo_attr TYPE REF TO if_ixml_node, lv_aname TYPE string, lv_avalue TYPE string, lv_aidx TYPE i, lv_alen TYPE i. lo_elements = lo_document->get_elements_by_tag_name( 'order' ). lo_node = lo_elements->get_item( 0 ). lo_element ?= lo_node. " 모든 속성 맵 가져오기 lo_attrs = lo_element->get_attributes( ). lv_alen = lo_attrs->get_length( ). lv_aidx = 0. WHILE lv_aidx < lv_alen. lo_attr = lo_attrs->get_item( lv_aidx ). lv_aname = lo_attr->get_name( ). lv_avalue = lo_attr->get_value( ). WRITE: / | 속성: { lv_aname } = { lv_avalue }|. lv_aidx = lv_aidx + 1. ENDWHILE. " → 속성: id = 1001 " → 속성: status = ACTIVE " → 속성: amount = 99.504. 문자열 파싱 FIND / SPLIT 조합
외부 라이브러리 없이
FIND,SPLIT,SUBSTRING만으로 고정 포맷 텍스트에서 속성값을 추출하는 방법이다.4-1. FIND + 오프셋·길이 활용
DATA: lv_text TYPE string, lv_value TYPE string, lv_offset TYPE i, lv_length TYPE i, lv_start TYPE i. lv_text = '<meta name="description" content="SAP ABAP 개발 가이드" charset="UTF-8">'. " ── content 속성값 추출 ─────────────────────────────────────────── " 1단계: 'content="' 위치 탐색 FIND 'content="' IN lv_text MATCH OFFSET lv_offset MATCH LENGTH lv_length. IF sy-subrc = 0. " 2단계: 속성값 시작 위치 = content=" 끝 바로 다음 lv_start = lv_offset + lv_length. " 3단계: 닫는 따옴표까지 길이 계산 FIND '"' IN SECTION OFFSET lv_start OF lv_text MATCH OFFSET lv_offset. " 4단계: 잘라내기 lv_length = lv_offset - lv_start. lv_value = lv_text+lv_start(lv_length). WRITE: / |content = { lv_value }|. " → SAP ABAP 개발 가이드 ENDIF.4-2. SPLIT을 이용한 속성 토큰 분리
DATA: lv_tag TYPE string, lt_tokens TYPE TABLE OF string, lv_token TYPE string, lt_kv TYPE TABLE OF string, lv_key TYPE string, lv_val TYPE string. " <...> 안의 내용만 잘라내기 lv_tag = '<input type="text" id="userId" value="admin" disabled>'. FIND REGEX '<([^>]+)>' IN lv_tag SUBMATCHES lv_tag. " 공백으로 토큰 분리 (첫 번째 = 태그명) SPLIT lv_tag AT ' ' INTO TABLE lt_tokens. LOOP AT lt_tokens INTO lv_token. " 값 없는 속성(disabled 등) 제외 IF lv_token CS '='. " = 기준 분리 SPLIT lv_token AT '=' INTO lv_key lv_val. " 따옴표 제거 REPLACE ALL OCCURRENCES OF '"' IN lv_val WITH ''. REPLACE ALL OCCURRENCES OF '''' IN lv_val WITH ''. CONDENSE lv_key NO-GAPS. WRITE: / | { lv_key } → { lv_val }|. ENDIF. ENDLOOP. " → type → text " → id → userId " → value → admin5. 유틸리티 클래스 재사용 가능한 캡슐화
위의 패턴을
lcl_attr_extractor로컬 클래스로 묶어 어디서나 간편하게 호출할 수 있도록 한다.5-1. 클래스 정의
CLASS lcl_attr_extractor DEFINITION. PUBLIC SECTION. TYPES: BEGIN OF ty_attr, name TYPE string, value TYPE string, END OF ty_attr. TYPES: tt_attr TYPE STANDARD TABLE OF ty_attr WITH DEFAULT KEY. " 특정 속성 하나 추출 (REGEX) CLASS-METHODS get_attr IMPORTING iv_html TYPE string iv_attr_name TYPE string RETURNING VALUE(rv_value) TYPE string. " 특정 태그의 모든 속성 추출 (첫 번째 매칭 태그) CLASS-METHODS get_all_attrs IMPORTING iv_html TYPE string iv_tag_name TYPE string RETURNING VALUE(rt_attrs) TYPE tt_attr. " 특정 태그의 반복 속성값을 테이블로 수집 CLASS-METHODS collect_attr IMPORTING iv_html TYPE string iv_tag_name TYPE string iv_attr_name TYPE string RETURNING VALUE(rt_values) TYPE string_table. ENDCLASS. CLASS lcl_attr_extractor IMPLEMENTATION. METHOD get_attr. " 패턴: attr_name\s*=\s*["']([^"']*)["'] DATA lv_pattern TYPE string. lv_pattern = iv_attr_name && '\s*=\s*["\x27]([^"\x27]*)["\x27]'. FIND REGEX lv_pattern IN iv_html SUBMATCHES rv_value. ENDMETHOD. METHOD get_all_attrs. " 1. 해당 태그 전체 텍스트 추출 DATA: lv_tag_text TYPE string, lo_regex TYPE REF TO cl_abap_regex, lo_matcher TYPE REF TO cl_abap_matcher, ls_attr TYPE ty_attr, lv_pattern TYPE string. lv_pattern = '<' && iv_tag_name && '\s([^>]*)'. FIND REGEX lv_pattern IN iv_html SUBMATCHES lv_tag_text. CHECK sy-subrc = 0. " 2. key=value 쌍 반복 추출 lo_regex = cl_abap_regex=>create( pattern = '(\w[\w\-]*)\s*=\s*["\x27]([^"\x27]*)["\x27]' ignore_case = abap_true ). lo_matcher = lo_regex->create_matcher( text = lv_tag_text ). WHILE lo_matcher->find_next( ) = abap_true. ls_attr-name = lo_matcher->get_submatch( 1 ). ls_attr-value = lo_matcher->get_submatch( 2 ). APPEND ls_attr TO rt_attrs. ENDWHILE. ENDMETHOD. METHOD collect_attr. " 패턴: <tag_name[^>]*attr_name="value" DATA: lv_pattern TYPE string, lo_regex TYPE REF TO cl_abap_regex, lo_matcher TYPE REF TO cl_abap_matcher, lv_value TYPE string. lv_pattern = '<' && iv_tag_name && '[^>]*' && iv_attr_name && '\s*=\s*["\x27]([^"\x27]*)["\x27]'. lo_regex = cl_abap_regex=>create( pattern = lv_pattern ignore_case = abap_true ). lo_matcher = lo_regex->create_matcher( text = iv_html ). WHILE lo_matcher->find_next( ) = abap_true. lv_value = lo_matcher->get_submatch( 1 ). APPEND lv_value TO rt_values. ENDWHILE. ENDMETHOD. ENDCLASS.5-2. 호출 예시
DATA: lv_html TYPE string, lv_val TYPE string, lt_attrs TYPE lcl_attr_extractor=>tt_attr, lt_srcs TYPE string_table, ls_attr TYPE lcl_attr_extractor=>ty_attr, lv_src TYPE string. lv_html = '<form action="/submit" method="POST" id="loginForm">' && '<input type="text" name="userId" value="admin"/>' && '<input type="password" name="password" value=""/>' && '<img src="logo.png" alt="로고"/>' && '<img src="banner.jpg" alt="배너"/>' && '</form>'. " ① 단일 속성값 추출 lv_val = lcl_attr_extractor=>get_attr( iv_html = lv_html iv_attr_name = 'action' ). WRITE: / |form action = { lv_val }|. " → /submit " ② <form> 태그 전체 속성 목록 lt_attrs = lcl_attr_extractor=>get_all_attrs( iv_html = lv_html iv_tag_name = 'form' ). LOOP AT lt_attrs INTO ls_attr. WRITE: / | { ls_attr-name } = { ls_attr-value }|. ENDLOOP. " → action = /submit " → method = POST " → id = loginForm " ③ <img> 태그 src 전체 수집 lt_srcs = lcl_attr_extractor=>collect_attr( iv_html = lv_html iv_tag_name = 'img' iv_attr_name = 'src' ). LOOP AT lt_srcs INTO lv_src. WRITE: / |img src = { lv_src }|. ENDLOOP. " → logo.png " → banner.jpg⚠️ HTML 인코딩 주의: 속성값에
&,<등 HTML 엔티티가 포함된 경우 추출 후 별도로 디코딩 처리가 필요하다.REPLACE ALL OCCURRENCES OF '&' IN lv_val WITH '&'형태로 처리한다.6. 패턴 요약 표
목적 핵심 코드 비고 단일 속성 추출 (REGEX) FIND REGEX 'attr\s*=\s*["\x27]([^"\x27]*)["\x27]'
IN lv_html SUBMATCHES lv_val.가장 간단 전체 반복 수집 FIND ALL OCCURRENCES OF REGEX …
IN lv_html RESULTS lt_match.복수 태그 OOP REGEX cl_abap_regex=>create( pattern = … )
lo_matcher->find_next( )
lo_matcher->get_submatch( 1 )루프 매칭 XML DOM (표준) cl_ixml + get_elements_by_tag_name
lo_element->get_attribute( 'attr' )Well-formed XML 전체 속성 열거 lo_element->get_attributes( )
lo_attrs->get_item( idx )->get_name/value속성명 미정 시 문자열 파싱 FIND 'attr="' MATCH OFFSET …
lv_text+start(length)고정 포맷 자주 쓰는 REGEX 패턴 설명 attr\s*=\s*["\x27]([^"\x27]*)["\x27]속성값 추출 (큰따옴표·작은따옴표 모두) <tagname[^>]*>특정 태그 전체 매칭 (\w[\w\-]*)\s*=\s*["\x27]([^"\x27]*)["\x27]key=value 쌍 전체 수집 <tagname[^>]*attr="([^"]*)"특정 태그의 특정 속성 조합 'SAP > 소스' 카테고리의 다른 글
ABAP Nugget Viewer (0) 2026.06.04 Z_MYBATIS_TO_ABAP — 설계 및 설치 가이드 (0) 2026.05.27 AMDP SQLScript 날짜·시간 계산 함수 예제 (0) 2026.05.25 ABAP PascalCase 변환 — snake_case ↔ Table Field (0) 2026.05.19 Z_MMZCL_CONV — 메소드별 로직 설명 (1) 2026.05.17