将 JSON xstring 反序列化为 ABAP 中的结构

Deserializing JSON xstring to structure in ABAP

我想将 JSON 字符串反序列化为如下定义的结构:

TYPES: BEGIN OF json_subobject,
         c TYPE i,
         d TYPE decfloat34,
       END OF json_subobject.
TYPES: BEGIN OF json_object,
        a TYPE c LENGTH 10,
        b TYPE json_subobject,
       END OF json_object.

DATA: foo TYPE json_object.

我在 JSON 中对这个结构建模如下:

{
  "a":"FooBar",
  "b":{
    "c":9,
    "d":3.14
  }
}

现在,我写了一个简单的程序来尝试上面JSON 上的CALL TRANSFORMATION 语句以及上面的结构定义。该程序应该反序列化 JSON(硬编码到变量 lv_xmls 中)并打印出结果结构的内容。结构的内容应该与原始JSON的内容相匹配。这是程序:

TYPES: BEGIN OF json_subobject,
         c TYPE i,
         d TYPE decfloat34,
       END OF json_subobject.
TYPES: BEGIN OF json_object,
         a TYPE c LENGTH 10,
         b TYPE json_subobject,
       END OF json_object.

DATA: foo TYPE json_object.

DATA: lv_xmls TYPE string VALUE '{"a":"FooBar","b":{"c":9,"d":3.14}}',
      lv_xmlb TYPE xstring.

TRY.
    lv_xmlb = cl_abap_codepage=>convert_to(
                source      = lv_xmls
                codepage    = `UTF-8`
                endian      = space
                replacement = '#'
                ignore_cerr = abap_false ).
  CATCH cx_parameter_invalid_range cx_sy_codepage_converter_init cx_sy_conversion_codepage cx_parameter_invalid_type.
    ENDTRY.

WRITE: |Deserializing JSON ...|.
NEW-LINE.

CALL TRANSFORMATION id SOURCE XML lv_xmlb RESULT XML = foo.

WRITE: '{'.
NEW-LINE.
WRITE: |  "a": "{ foo-a }",|.
NEW-LINE.
WRITE: |  "b": \{|.
NEW-LINE.
WRITE: |    "c": { foo-b-c },|.
NEW-LINE.
WRITE: |    "d": { foo-b-d },|.
NEW-LINE.
WRITE: |  \}|.
NEW-LINE.
WRITE: |\}|.

我期望的输出是:

Deserializing JSON ...
{
  "a":"FooBar",
  "b":{
    "c":9,
    "d":3.14
  }
}

但不幸的是,我得到的输出是:

Deserializing JSON ...
{
  "a":"",
  "b":{
    "c":0,
    "d":0
  }
}

看起来 CALL TRANSFORMATION 语句对我没有任何作用。

有没有 ABAP 大师可以告诉我如何在这个(希望如此)简单的案例中使用 CALL TRANSFORMATION?我已经环顾四周,发现 this helpful GitHub repo 演示了如何将 JSON 反序列化为 class,但为了简单起见,我更愿意将 JSON 反序列化为结构。我不完全确定如何使用 CALL TRANSFORMATION,因为我不熟悉 XSLT 和高级 XML 功能,所以如果可能,我将非常感谢现成的解决方案...

非常感谢,

约书亚

我不是一个好的 ABAP 开发者,所以我不知道是否有更好的代码来做这件事,但这对我有用:

    TYPES: BEGIN OF json_subobject,
         c TYPE i,
         d TYPE decfloat34,
       END OF json_subobject.
TYPES: BEGIN OF json_object,
         a TYPE c LENGTH 10,
         b TYPE json_subobject,
       END OF json_object.

DATA: foo TYPE json_object,
      writer TYPE REF TO cl_sxml_string_writer,
      json TYPE xstring.

DATA: lv_xmls TYPE string VALUE '{"a":"FooBar","b":{"c":9,"d":3.14}}',
      lv_xmlb TYPE xstring.

*TRY.
*    lv_xmlb = cl_abap_codepage=>convert_to(
*                source      = lv_xmls
*                codepage    = `UTF-8`
*                endian      = space
*                replacement = '#'
*                ignore_cerr = abap_false ).
*  CATCH cx_parameter_invalid_range cx_sy_codepage_converter_init cx_sy_conversion_codepage cx_parameter_invalid_type.
*    ENDTRY.



WRITE: |Deserializing JSON ...|.
NEW-LINE.

cl_fdt_json=>json_to_data( EXPORTING iv_json = lv_xmls
CHANGING ca_data = foo ).

WRITE: '{'.
NEW-LINE.
WRITE: |  "a": "{ foo-a }",|.
NEW-LINE.
WRITE: |  "b": \{|.
NEW-LINE.
WRITE: |    "c": { foo-b-c },|.
NEW-LINE.
WRITE: |    "d": { foo-b-d },|.
NEW-LINE.
WRITE: |  \}|.
NEW-LINE.
WRITE: |\}|.

此代码只是将 JSON 数组转换为内部 table。

我的回答分为两部分。根据我的说法,第一个是最适合 JSON 的解决方案,第二部分解释了为什么您的代码不适用于 CALL TRANSFORMATION ID.

第 1 部分:

您最好使用更适合处理 JSON 的 SAP classes 之一。我更喜欢 class /UI2/CL_JSON,根据我的说法,这是最 "open" class,因为它是 SAP 宣传最多的(见下面的文档),虽然没有官方支持(是的,这很难理解,欢迎来到 SAP 世界。

TYPES: BEGIN OF json_subobject,
         c TYPE i,
         d TYPE decfloat34,
       END OF json_subobject.
TYPES: BEGIN OF json_object,
         a TYPE c LENGTH 10,
         b TYPE json_subobject,
       END OF json_object.

DATA: foo TYPE json_object.

/ui2/cl_json=>deserialize(
    EXPORTING json = '{"a":"FooBar","b":{"c":9,"d":3.14}}'
    CHANGING  data = foo ).

ASSERT foo = VALUE json_object( 
    a   = 'FooBar' 
    b-c = 9 
    b-d = '3.14' ).

更多信息参考文档:


第 2 部分:

您使用 CALL TRANSFORMATION ID 的代码不起作用有两个原因:

  1. JSON 必须始终是一个 JSON 对象,其成员以根名称 (RESULT rootname1 = var1 rootname2 = var2) 命名。您只定义了一个名为 "XML" 的根,因此 JSON 应该类似于 {"X-ML":...}(它是 X-ML 而不是 XML 因为特定的 ABAP reason )
  2. ABAP 组件名称以大写字母(A、B、C、D)内部存储在 SAP 中,身份转换 ID 区分大小写成员名称,因此 JSON 数据中的成员名称应为大写(您的所有名称均为小写 a、b、c、d)。

换句话说,如果您的输入 JSON 包含以下值,您的代码可能会工作:

lv_xmls = '{"X-ML":{"A":"FooBar","B":{"C":9,"D":3.14}}}'.

另一种解决方案可能包括创建自定义身份转换,将 JSON 成员名称转换为大写并添加虚拟根元素。但那是另外一回事了。