美文网首页SAPSAP 实用篇
答网友疑问:ABAP Function Module 如何支持内

答网友疑问:ABAP Function Module 如何支持内

作者: 华山令狐冲 | 来源:发表于2024-01-06 09:33 被阅读0次

有朋友向我咨询:

jerry老师,我需要用se37开发一个函数,要求就是函数的入参是一个动态的内表,就是入参的内表结构不定,数据量也不定。这种函数的入参定义啊?我需要的效果就是在这个函数里面,对这个动态的入参内表进行一个LOOP循环,在循环里面进行数据变更保存。


在一般情况下,我们在 SE37 创建 Function Module 时,对于其输入参数的数据结构,都是确认已知的。但实际项目中也会遇到,需要编写一个通用的 Function Module,能处理具有各种各样行项目结构(table line structure)的内表。

本文就来解答朋友提出的这个疑问,在 ABAP 里应该如何编写。

第一步也是最重要的一步。这个 Function Module 的输入参数,应该如何指定?

如下图所示,我定义一个名叫 IT_TABLE 的输入参数,I 代表 Input 即输入,T 代表 Table Type 即 Table 类型。这个是 ABAP 编程的命名规范。这个输入参数的名称也可以改成其他的,只要符合大家开发团队指定的规范即可。

最关键的一步,就是将这个输入参数的类型,指定成 ANY TABLE.

这是一个泛型,即 ABAP 帮助文档里所谓的 Generic Type,可以代表一切类型的 ABAP 内表。

下一步,我们要开始实现这个函数体了,这里要用到 ABAP RTTI 的理论知识。有过 Java 编程语言开发经验的朋友,可以把 ABAP RTTI 类比成 Java 的反射机制,二者虽然是不同的编程语言,但这两个名词实际上描述的都是同一种机制,即在运行时,通过编程语言提供的 API,来获取数据对象的各种信息。

ABAP RTTI 的全称是 Runtime Type Identification. 使用 RTTI,ABAP 开发人员可以在运行时获取关于数据对象的各种信息,包括其数据类型、字段列表(如果数据是Structure 或者内表的话)以及这些字段的技术属性,比如字段名称,字段数据类型,长度等等。对于这些字段信息,ABAP 提供了一组特殊的类和接口,包括 CL_ABAP_TYPEDESCR、CL_ABAP_STRUCTDESCR、CL_ABAP_TABLEDESCR 等等。

我们来动手实践一下。

这个函数的全部源代码:

FUNCTION zdynamic_table_input.
  FIELD-SYMBOLS:<item>  TYPE any,
                <value> TYPE any.

  DATA: lo_table_descr TYPE REF TO cl_abap_tabledescr,
        lo_struc_descr TYPE REF TO cl_abap_structdescr,
        lt_fields      TYPE abap_compdescr_tab,
        ls_field       TYPE abap_compdescr,
        lv_field_name  TYPE abap_compname.

  lo_table_descr ?= cl_abap_tabledescr=>describe_by_data( it_table ).

  lo_struc_descr ?= lo_table_descr->get_table_line_type( ).
  lt_fields = lo_struc_descr->components.
  LOOP AT it_table ASSIGNING <item>.
    LOOP AT lt_fields INTO ls_field.
      ASSIGN COMPONENT ls_field-name OF STRUCTURE <item> TO <value>.
      WRITE:/ 'field name:', ls_field-name, ' value: ', <value>.
    ENDLOOP.
  ENDLOOP.

ENDFUNCTION.

关键点有 4 处,我们逐一讲解。

  1. 代码第 17 行:lo_table_descr ?= cl_abap_tabledescr=>describe_by_data( it_table ).

这里调用 API cl_abap_tabledescr=>describe_by_data,将输入参数,即内表 it_table 的描述符取出来,存储到变量 lo_table_descr 中去。

何为描述符(descriptor)?描述符是一个对象引用,封装了被描述的 ABAP 数据对象的所有技术类型信息。我们拿到了这个对象应用,就可以用其暴露的各种 get 开头的公有方法,拿到被描述的 ABAP 数据对象的各种信息。

注意 cl_abap_tabledescr=>describe_by_data 返回的描述符,类型是 cl_abap_typedescr,它是所有 ABAP RTTI API 里各种其他描述符 cl_abap_XXXdescr 共同的父类。在这一行代码里,因为我们很肯定,it_table 一定是个内表,因为它受 Function Module 输入参数类型 ANY TABLE 所约束,所以我们在代码里声明了一个类型为 cl_abap_tabledescr 的变量,lo_table_descr, 去接收 cl_abap_tabledescr=>describe_by_data 的返回结果。

这里我们使用了一个类型转换符号 ?=, 符号的左边是子类对象类型,符号的右边是父类对象类型。

  1. 代码第 19 行,拿到了内表的类型描述符之后,我们调用其 get_table_line_type, 拿到内表行项目的类型描述符,存储到类型为 cl_abap_structdescr 的结构体类型描述符,lo_struc_descr 中去。

  2. 代码第 20 行,通过结构体类型描述符,我们就能在运行时,知道这个 structure 详细的字段信息了。

我们动手写个报表来测试一下,看运行时 structure 的字段信息长啥样。

REPORT zst.

TYPES: BEGIN OF ty_line,
         name TYPE string,
         age  TYPE int4,
       END OF ty_line.

TYPES: tt_table TYPE TABLE OF ty_line.

DATA: ls_line  TYPE ty_line,
      lt_table TYPE tt_table,
      lt_tadir TYPE TABLE OF tadir.

ls_line-name = 'Jerry'.
ls_line-age = 41.
APPEND ls_line TO lt_table.

ls_line-name = 'Tom'.
ls_line-age = 50.
APPEND ls_line TO lt_table.

CALL FUNCTION 'ZDYNAMIC_TABLE_INPUT'
  EXPORTING
    it_table = lt_table.

上面这段代码,我自定义了一个 structure 类型,包含 name 和 age 两个字段。基于这个行项目结构我创建了一个内表,插入两条记录到内表里,然后调用我们还没有编写完毕的函数。

单步调试:

从这个内表里,我们拿到了 Function Module 输入参数的内表行项目的字段名称列表:NAME 和 AGE.


有了这些名称,剩下一步就很简单了:使用 ASSIGN COMPONENT <字段名称> OF STRUCTURE <item> TO <value>. 将内表行项目对应字段的实际值,赋给 Field Symbol 变量。

因为在编写代码的时候,我们是不知道这些实际值的数据类型的,因此代码第 9 行定义的 Field Symbol,必须使用另一种泛型 any,这里的语言是代表一切 ABAP 数据类型。

最后测试结果,可以把这个内表里两条记录的每个字段名称和字段值全部打印出来。

为了验证这个 Function Module 是否真的支持任意内表类型,我们把输入内表变量更换成一个更复杂的结构,比如行项目为 TADIR 这张表对应的字段:

SELECT * INTO TABLE lt_tadir FROM tadir UP TO 2 ROWS .

CALL FUNCTION 'ZDYNAMIC_TABLE_INPUT'
  EXPORTING
    it_table = lt_tadir.

看看运行时传入 Function Module 的这个内表:

最后的输出结果:

证明咱们这个函数编写成功了。

相关文章

网友评论

    本文标题:答网友疑问:ABAP Function Module 如何支持内

    本文链接:https://www.haomeiwen.com/subject/crfegdtx.html