2023 Aug 04 11:21 AM
Hi Folks,
another question regarding the code within a simple transformation, where I am transforming a deep ABAP structure into a JSON string.
The parent ABAP structure contains, as well as some string fields, an embedded flat structure:
ACCESS_TOKEN Types ZDE_ACCESS_TOKEN STRING
USER_GSTIN Types ZDE_USER_GSTIN STRING
DATA_SOURCE Types ZDE_DATA_SOURCE STRING
TRANSACTION_DETAILS Types ZSTY_TRANS_DETAILS
The embedded "transaction_details" structure contains string fields:
SUPPLY_TYPE Types ZDE_SUPPLY_TYPE STRING
CHARGE_TYPE Types ZDE_CHARGE_TYPE STRING
This is what the critical part of my transformation code (in XSLT) looks like:
<str name="data_source">
<tt:value ref="einvoice_request.data_source"/>
</str>
<transaction_details>
<object>
<str name="supply_type">
<tt:value ref="einvoice_request.transaction_details.supply_type"/>
</str>
When debugging, as soon as the transformation hits the "transaction_details" element, it fails. Obviously the code is incorrect, but I don't know how to declare the "transaction_details" element as an embedded flat structure.
Thanks for reading,
JM
At the risk of sounding patronising (which I don't mean to be), I hope @sandra.rossi reads this.
2023 Aug 04 12:40 PM
EDIT : please forget my answer, it's only valid for XML but the question is about JSON.
Use tt:ref to change the current data node:
<transaction_details tt:ref="TRANSACTION_DETAILS"><br>
See ABAP documentation - ST - tt:ref, Current Node
NB: that's weird to have the XML with ABAP names in lower case, instead of camel case e.g., TransactionDetails...
2023 Aug 04 3:14 PM
Hi Sandra,
I tried your suggestion, and it flags a syntax error.
However, I tried this:
<object name="transaction_details">
...
</object>
And it seems to work. At least, it doesn't fall over during debugging, although I haven't got as far as the resulting JSON string to be able to check, because...
...unfortunately, I now have a problem with an embedded table type:
<array>
<tt:loop ref="einvoice_request.item_list">
<object>
<num name="item_serial_number">
<tt:value ref="$ref.item_serial_number"/>
</num>
The root structure is "einvoice_request", and the embedded table field name is "item_list". I copied the loop format from DEMO_ST_JSON_TABLE, but although it activates OK (almost anything seems to activate in ST) my version falls over as soon as it hits the <object> element. In this case, I don't see that I can give a reference (or a name) to the object, as it's just a line of the "item_list" table. If I remove the <object> element, it falls over when it hits the element "item_serial_number" (the value passed to "item_serial_number" is "000010", BTW).Re. lower/camel case, I read somewhere that when doing a transform to JSON-XML, that's how it should be - there's a lot of misleading information out there. Lack of camel-case doesn't seem to affect it though.Any ideas on the table loop? Cheers,John
2023 Aug 04 4:16 PM
Sorry, forget my answer, I was way too fast, and replied on XML, although you said it was JSON.
2023 Aug 04 4:21 PM
<array> alone is weird, probably it should contain a name="..."
but difficult to say, it depends on the exact data type of your input variable.
2023 Aug 04 4:27 PM
And yet this is part of the only ABAP to JSON demo ST (DEMO_ST_JSON_TABLE) with a table loop:
<array>
<tt:loop ref=".CARRIERS">
<object>
<str name="Carrier-Id">
<tt:value ref="$ref.carrid"/>
</str>
Which is what I copied...My source data is a (deep) structure, with a table type field. The table type has a flat structure as the line type.I'll experiment with naming the array and report back.
2023 Aug 04 5:03 PM
I know very well ST, so if you need help... (I'll try to better read your question, next time)
2023 Aug 04 5:04 PM
Named the array, no difference.
Cannot fathom why it doesn't like the <object> line: there are a lot of ABAP to JSON examples which contain almost identical source code, certainly in terms of the relationship between the table array and the table line. I've got an old XML ST which is way more complex, with loads of nested tables and structures, and it works like a dream. Seems that the JSON STs are a whole lot trickier to write.
2023 Aug 05 9:45 AM
Difficult to say, so I advise to convert your ABAP structure to JSON-XML, and compare it with what you have in your ST transformation.
Use this to get JSON-XML:
DATA(json_xml) = VALUE xstring( ).
PERFORM convert_dobj_to_json_xml USING 'DUMMY' your_abap_variable CHANGING json_xml.
FORM convert_dobj_to_json_xml
USING
rootname TYPE clike
dobj TYPE any
CHANGING
json_xml TYPE xstring.
" Convert first DOBJ to JSON
DATA(writer) = cl_sxml_string_writer=>create( type = if_sxml=>co_xt_json ).
DATA(srcbind_tab) = VALUE abap_trans_srcbind_tab( ( name = rootname value = REF #( dobj ) ) ).
CALL TRANSFORMATION id SOURCE (srcbind_tab) RESULT XML writer.
DATA(json) = writer->get_output( ).
" Convert JSON to JSON-XML
CALL TRANSFORMATION id SOURCE XML json RESULT XML json_xml OPTIONS xml_header = 'no'.
ENDFORM.
For information, here's the result you would get with fixed data:
REPORT.
DATA: BEGIN OF lt_data OCCURS 0,
carrierid TYPE s_carr_id,
seats TYPE STANDARD TABLE OF char10 WITH EMPTY KEY,
END OF lt_data.
lt_data[] = VALUE #(
( carrierid = '1' seats = VALUE #( ( '56' ) ( '67' ) ) )
( carrierid = '2' seats = VALUE #( ( '32' ) ( '89' ) ) )
).
DATA(json_xml) = VALUE xstring( ).
PERFORM convert_dobj_to_json_xml USING 'DATA' lt_data[] CHANGING json_xml.
ASSERT cl_abap_codepage=>convert_from( json_xml )
= '<object><array name="DATA">'
&& '<object><str name="CARRIERID">1</str><array name="SEATS"><str>56</str><str>67</str></array></object>'
&& '<object><str name="CARRIERID">2</str><array name="SEATS"><str>32</str><str>89</str></array></object>'
&& '</array></object>'.
DATA(writer) = cl_sxml_string_writer=>create( type = if_sxml=>co_xt_json ).
CALL TRANSFORMATION id SOURCE XML json_xml RESULT XML writer.
DATA(json) = writer->get_output( ).
ASSERT cl_abap_codepage=>convert_from( json )
= '{"DATA":[{"CARRIERID":"1","SEATS":["56","67"]},{"CARRIERID":"2","SEATS":["32","89"]}]}'.
2023 Aug 05 2:44 PM
Hi Sandra,
that's a really neat piece of code, and it highlighted a difference between my ST and the resulting JSON-XML. Here's the result of your code:
<array name="ITEM_LIST">
<object>
<str name="ITEM_SERIAL_NUMBER">123 </str>
And here's my original ST for comparison: <array>
<tt:loop ref="einvoice_request.item_list">
<object>
<num name="item_serial_number">
I thought maybe the clue was in the levels of indent: the JSON-XML only has two (array, object), whereas my ST has three (array, loop, object).I had already tried removing the <object> element, and that didn't work. Removing the <tt:loop> element prevents it from activating. So finally, I removed the <array> element. Which seems crazy, because every other ST I can find in SAP uses <array> for nested tables. Still, no matter. I changed the ST to look like this: <tt:loop ref="einvoice_request.item_list">
<object name="item_list">
<num name="item_serial_number">
<tt:value ref="$ref.item_serial_number"/>
And the transformation ran without any errors. However, the item_list element isn't treated as an array, only as a single iteration - I was hoping for [ { : },
"item_list": {
"item_serial_number": 123,
So I substituted 'array' for 'object' like this: <tt:loop ref="einvoice_request.item_list">
<array name="item_list">
<num name="item_serial_number">
Now the ST throws an error: "Executing the 'OpenAttribute' operation leads to an invalid XML document". The error is triggered when executing the <num name="item_serial_number"> element. I tried changing the 'num' to 'str', but I get the same error. If I swapped around the <tt:loop> and <array>, I got the same error.So I went back to my "3 level" approach, but this time I reversed the <loop and <array elements: <tt:loop ref=".einvoice_request.item_list">
<array name="item_list">
<object>
<str name="item_serial_number">
<tt:value ref="$ref.item_serial_number"/>
And at last, it works. Here's the result: },
"item_list": [
{
"item_serial_number": "000010",
So thank you Sandra for all your help: it was really appreciated, and it set me on the right track, plus I learned some really useful (and clever) ABAP. And it's odd that the DEMO_ST_JSON_TABLE ST I mentioned earlier has the <array> and <tt:loop ref> elements the opposite way round, but it didn't work for me.I hope this posting will be useful for some other lost soul.Best regards,John
2023 Aug 05 5:52 PM
Thanks for the feedback.
I don't understand how you can say that <tt:loop> before <array> can work. If you have 2 items in the array/internal table, although I can't generalize, your code would fail (it depends on the exact context).
The normal order is:
<array ...
<tt:loop ...
If you have a "table of table", you might have this:
<array ...
<tt:loop ...
<array ...
<tt:loop ...
2023 Aug 07 11:28 AM
Hi Sandra,
my ST (above) was the only way I found to avoid an exception when calling the transformation. And the JSON output looked fine, but my source data only contained 1 row in the nested table...
After your last comment, I added a second row to the nested table (in the debugger), just to re-check the output.
And as you predicted, the output was incorrect. It produced 2 serialised entries for the item list table, but expressed (in the JSON string) as 2 separate tables, each with 1 row.
So, back to the beginning... this was my original ST code (at the beginning, I also tried with the first data element like <num name ="item_serial_number">)
<array>
<tt:loop ref=".einvoice_request.item_list">
<object>
<str name="item_serial_number">
<tt:value ref="$ref.item_serial_number"/>
</str>
Every time, it triggered exception cx_st_match_element or exception cx_transformation_error after the <object> element.
So I reversed nearly all my changes, but retained the name of the array:
<array name="item_list">
<tt:loop ref=".einvoice_request.item_list">
<object>
<str name="item_serial_number">
<tt:value ref="$ref.item_serial_number"/>
</str>
And this version works (!). The output is correct, at last. But the only difference is that the array has a name, so I'm still a bit surprised.Thanks again for all your help - I couldn't have done it without your advice.Best regards,John