ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • ABAP 동적 JSON 처리 - LCL_JSON_HANDLER 로컬 클래스 설계
    SAP/소스 2026. 7. 7. 21:27

    abap_json_handler.pdf
    0.09MB
    abap_json_handler.html
    0.02MB

     

     

    ABAP 동적 JSON 처리 - LCL_JSON_HANDLER 로컬 클래스 설계

    ABAP 동적 JSON 처리: LCL_JSON_HANDLER 로컬 클래스 설계

    SAP ABAP 환경에서 JSON String을 동적 구조(REF TO DATA)로 변환하고, 구조체를 JSON으로 직렬화하는 로직을 /ui2/cl_json 기반으로 모듈화한 과정을 정리한다. 펑션 그룹/리포트 양쪽에서 재사용 가능한 로컬 클래스, 인스턴스 기반 구조로 설계했다.

    1. 요구사항 정리

    • 동적 데이터 전달 방식으로 JSON을 채택
    • 값 변환은 /ui2/cl_json 표준 클래스 사용
    • JSON String → 동적 구조 변환 시 REF TO DATA 타입으로 리턴
    • 구조체 → JSON 변환은 직접 매핑 방식
    • 각 로직은 class method로 모듈화
    • 클래스는 로컬로 작성하여 펑션/리포트 어디서든 INCLUDE 가능하게, 인스턴스로 생성해 활용

    2. 설계 원칙

    JSON → ABAP은 타입을 모르는 상태에서 출발하므로 /ui2/cl_json=>generate()로 런타임에 타입을 결정해 REF TO DATA를 생성한다. 반대로 ABAP → JSON은 이미 정의된 구조체를 그대로 직렬화(serialize())하면 되므로 별도의 동적 타입 판별이 필요 없다.

    💡 struc → itab → field 계층 구조를 다룰 때는 cl_abap_typedescr=>describe_by_data()로 얻은 kind 값(kind_elem / kind_struct / kind_table)을 기준으로 재귀 순회하는 것이 기본 패턴이다. 다만 /ui2/cl_json=>generate()를 쓰면 이 재귀 타입 생성 과정을 라이브러리가 대신 처리해준다.

    3. 최종 소스: LCL_JSON_HANDLER (로컬 클래스, 인스턴스 기반)

    펑션 그룹의 INCLUDE나 리포트 TOP-INCLUDE 어디에 넣어도 동작하도록 구성했다. 예외 클래스(lcx_json_error)와 결과 구조(ty_json_result)를 함께 정의하고, CREATE OBJECT로 인스턴스를 만들어 메소드를 호출하는 구조다.

    주요 메소드 구성

    메소드역할
    json_to_dynamic_dataJSON String → REF TO DATA (동적 구조 생성)
    struc_to_json구조체(정적 타입) → JSON String 직렬화
    get_field_value동적 데이터에서 특정 필드값 조회
    get_type_kindREF TO DATA의 TYPE KIND(elem/struct/table) 판별
    is_valid_jsonJSON 형식 간단 유효성 체크
    convert_with_error_handling변환+예외처리를 하나로 묶은 통합 래퍼
    ⚠️ nested itab(구조 안에 배열이 중첩된 경우) 내부 라인 순회 로직(get_table_lines 등)은 아직 포함되지 않았다. 실제 JSON에 배열 중첩이 있다면 RTTI 재귀 로직을 별도로 추가해야 한다.

    체크리스트

    • naming 매핑(camelCase vs none) 확인 후 constructor 파라미터로 지정
    • 날짜/시간 필드 포맷(JSON 문자열 ↔ DATS/TIMS) 사전 확인
    • 대량 데이터 처리 시 RTTI/동적 ASSIGN 성능 저하 고려
    • 순환 참조/과도한 nested 구조에 대한 재귀 깊이 제한 필요 여부 검토

    4. 전체 소스 코드

    *&---------------------------------------------------------------------*
    *&  LCL_JSON_HANDLER
    *&  - JSON String -> 동적 구조(REF TO DATA) 변환
    *&  - 구조체/테이블 -> JSON String 변환
    *&  - RTTI 기반 동적 필드 접근 유틸
    *&  * 리포트 TOP-INCLUDE 또는 함수그룹 LCL_INCLUDE 어디든 삽입 가능
    *&---------------------------------------------------------------------*
    
    "----------------------------------------------------------------------
    " 예외 클래스 (로컬)
    "----------------------------------------------------------------------
    CLASS lcx_json_error DEFINITION INHERITING FROM cx_static_check.
      PUBLIC SECTION.
        DATA mv_msg TYPE string.
    
        METHODS constructor
          IMPORTING
            iv_msg TYPE string OPTIONAL
            previous LIKE previous OPTIONAL.
    ENDCLASS.
    
    CLASS lcx_json_error IMPLEMENTATION.
      METHOD constructor.
        super->constructor( previous = previous ).
        mv_msg = iv_msg.
      ENDMETHOD.
    ENDCLASS.
    
    "----------------------------------------------------------------------
    " 결과 구조 (변환 결과 공통 리턴 타입)
    "----------------------------------------------------------------------
    TYPES: BEGIN OF ty_json_result,
             success TYPE abap_bool,
             message TYPE string,
             data    TYPE REF TO data,
           END OF ty_json_result.
    
    "----------------------------------------------------------------------
    " LCL_JSON_HANDLER 클래스 정의
    "----------------------------------------------------------------------
    CLASS lcl_json_handler DEFINITION.
    
      PUBLIC SECTION.
    
        "----------------------------------------------------
        " 생성자 : 기본 Naming 모드 설정 (camelCase / none 등)
        "----------------------------------------------------
        METHODS constructor
          IMPORTING
            iv_pretty_name TYPE /ui2/cl_json=>pretty_name_mode
                           DEFAULT /ui2/cl_json=>pretty_mode-camel_case.
    
        "----------------------------------------------------
        " ① JSON String -> 동적 구조 (REF TO DATA)
        "----------------------------------------------------
        METHODS json_to_dynamic_data
          IMPORTING
            iv_json         TYPE string
          RETURNING
            VALUE(rr_data)  TYPE REF TO data
          RAISING
            lcx_json_error.
    
        "----------------------------------------------------
        " ② 구조체(정적 타입) -> JSON String
        "----------------------------------------------------
        METHODS struc_to_json
          IMPORTING
            is_data        TYPE any
            iv_compress    TYPE abap_bool DEFAULT abap_true
          RETURNING
            VALUE(rv_json) TYPE string.
    
        "----------------------------------------------------
        " ③ 동적 데이터 내 특정 필드값 조회 (Structure 전제)
        "----------------------------------------------------
        METHODS get_field_value
          IMPORTING
            ir_data          TYPE REF TO data
            iv_fieldname     TYPE string
          RETURNING
            VALUE(rv_value)  TYPE string
          RAISING
            lcx_json_error.
    
        "----------------------------------------------------
        " ④ 동적 데이터의 TYPE KIND 판별 (elem/struct/table)
        "----------------------------------------------------
        METHODS get_type_kind
          IMPORTING
            ir_data        TYPE REF TO data
          RETURNING
            VALUE(rv_kind) TYPE abap_typekind.
    
        "----------------------------------------------------
        " ⑤ JSON 유효성 간단 체크
        "----------------------------------------------------
        METHODS is_valid_json
          IMPORTING
            iv_json         TYPE string
          RETURNING
            VALUE(rv_valid) TYPE abap_bool.
    
        "----------------------------------------------------
        " ⑥ 에러 핸들링 통합 래퍼 (호출부는 이 메소드 하나만 써도 됨)
        "----------------------------------------------------
        METHODS convert_with_error_handling
          IMPORTING
            iv_json          TYPE string
          RETURNING
            VALUE(rs_result) TYPE ty_json_result.
    
      PRIVATE SECTION.
        DATA mv_pretty_name TYPE /ui2/cl_json=>pretty_name_mode.
    
    ENDCLASS.
    
    
    "----------------------------------------------------------------------
    " LCL_JSON_HANDLER 구현
    "----------------------------------------------------------------------
    CLASS lcl_json_handler IMPLEMENTATION.
    
      METHOD constructor.
        mv_pretty_name = iv_pretty_name.
      ENDMETHOD.
    
    
      METHOD json_to_dynamic_data.
    
        IF me->is_valid_json( iv_json ) = abap_false.
          RAISE EXCEPTION TYPE lcx_json_error
            EXPORTING
              iv_msg = |Invalid JSON string|.
        ENDIF.
    
        TRY.
            /ui2/cl_json=>generate(
              EXPORTING
                json        = iv_json
                pretty_name = mv_pretty_name
              RECEIVING
                r_value     = rr_data ).
    
          CATCH cx_root INTO DATA(lx_error).
            RAISE EXCEPTION TYPE lcx_json_error
              EXPORTING
                iv_msg   = |JSON generate failed: { lx_error->get_text( ) }|
                previous = lx_error.
        ENDTRY.
    
        IF rr_data IS NOT BOUND.
          RAISE EXCEPTION TYPE lcx_json_error
            EXPORTING
              iv_msg = |JSON conversion returned no data|.
        ENDIF.
    
      ENDMETHOD.
    
    
      METHOD struc_to_json.
        rv_json = /ui2/cl_json=>serialize(
                    data        = is_data
                    pretty_name = mv_pretty_name
                    compress    = iv_compress ).
      ENDMETHOD.
    
    
      METHOD get_type_kind.
        DATA(lo_type) = cl_abap_typedescr=>describe_by_data_ref( ir_data ).
        rv_kind = lo_type->type_kind.
      ENDMETHOD.
    
    
      METHOD get_field_value.
    
        FIELD-SYMBOLS: <fs_struc> TYPE any,
                       <fs_field> TYPE any.
    
        ASSIGN ir_data->* TO <fs_struc>.
        IF sy-subrc <> 0.
          RAISE EXCEPTION TYPE lcx_json_error
            EXPORTING
              iv_msg = |Dereference failed|.
        ENDIF.
    
        ASSIGN COMPONENT iv_fieldname OF STRUCTURE <fs_struc> TO <fs_field>.
        IF sy-subrc <> 0.
          RAISE EXCEPTION TYPE lcx_json_error
            EXPORTING
              iv_msg = |Field { iv_fieldname } not found|.
        ENDIF.
    
        rv_value = <fs_field>.
    
      ENDMETHOD.
    
    
      METHOD is_valid_json.
    
        rv_valid = abap_true.
    
        IF iv_json IS INITIAL.
          rv_valid = abap_false.
          RETURN.
        ENDIF.
    
        DATA(lv_trim) = condense( iv_json ).
        IF NOT ( lv_trim(1) = '{' OR lv_trim(1) = '[' ).
          rv_valid = abap_false.
        ENDIF.
    
      ENDMETHOD.
    
    
      METHOD convert_with_error_handling.
    
        TRY.
            rs_result-data    = me->json_to_dynamic_data( iv_json ).
            rs_result-success = abap_true.
            rs_result-message = |Conversion successful|.
    
          CATCH lcx_json_error INTO DATA(lx_error).
            rs_result-success = abap_false.
            rs_result-message = lx_error->mv_msg.
        ENDTRY.
    
      ENDMETHOD.
    
    ENDCLASS.
    
    
    "----------------------------------------------------------------------
    " 호출부 사용 예시 (인스턴스 생성)
    "----------------------------------------------------------------------
    DATA: lo_json TYPE REF TO lcl_json_handler.
    
    CREATE OBJECT lo_json
      EXPORTING
        iv_pretty_name = /ui2/cl_json=>pretty_mode-camel_case.
    
    "--- JSON -> 동적 구조 ---
    TRY.
        DATA(lr_dynamic) = lo_json->json_to_dynamic_data(
                              '{"orderId":"1000","amount":250.5}' ).
    
        FIELD-SYMBOLS <fs> TYPE any.
        ASSIGN lr_dynamic->* TO <fs>.
    
      CATCH lcx_json_error INTO DATA(lx_err).
        WRITE: / lx_err->mv_msg.
    ENDTRY.
    
    "--- 구조체 -> JSON ---
    TYPES: BEGIN OF ty_order,
             order_id TYPE string,
             amount   TYPE p DECIMALS 2,
           END OF ty_order.
    
    DATA(ls_order) = VALUE ty_order( order_id = '1000' amount = '250.50' ).
    DATA(lv_json)  = lo_json->struc_to_json( ls_order ).
    
    "--- 통합 에러핸들링 래퍼 사용 ---
    DATA(ls_result) = lo_json->convert_with_error_handling( '{"a":1}' ).
    IF ls_result-success = abap_true.
      " ls_result-data 사용
    ELSE.
      " ls_result-message 로 에러 처리
    ENDIF.
    
    #SAP #ABAP #AMDP #RTTI #JSON #ui2_cl_json #로컬클래스 #동적데이터처리
Designed by Tistory.