Capture any standard report to JSON or XML? - json

I know that I can use LIST_TO_ASCI to convert a report to ASCII, but I would like to have a more high level data format like JSON, XML, CSV.
Is there a way to get something that is easier to handle then ASCII?
Here is the report I'd like to convert:
The conversion needs to be executed in ABAP on a result which was executed like this:
SUBMIT <REPORT_NAME> ... EXPORTING LIST TO MEMORY AND RETURN.

You can get access to SUBMIT list in memory like this:
call function 'LIST_FROM_MEMORY'
TABLES
listobject = t_list
EXCEPTIONS
not_found = 1
others = 2.
if sy-subrc <> 0.
message 'Unable to get list from memory' type 'E'.
endif.
call function 'WRITE_LIST'
TABLES
listobject = t_list
EXCEPTIONS
EMPTY_LIST = 1
OTHERS = 2
.
if sy-subrc <> 0.
message 'Unable to write list' type 'E'.
endif.
And the final step of the solution (conversion of result table to JSON) was already answered to you in your question.

I found a solution here: http://zevolving.com/2015/07/salv-table-22-get-data-directly-after-submit/
This is the code:
DATA: lt_outtab TYPE STANDARD TABLE OF alv_t_t2.
FIELD-SYMBOLS: <lt_outtab> like lt_outtab.
DATA lo_data TYPE REF TO data.
" Let know the model
cl_salv_bs_runtime_info=>set(
EXPORTING
display = abap_false
metadata = abap_false
data = abap_true
).
SUBMIT salv_demo_table_simple
AND RETURN.
TRY.
" get data from SALV model
cl_salv_bs_runtime_info=>get_data_ref(
IMPORTING
r_data = lo_data
).
ASSIGN lo_data->* to <lt_outtab>.
BREAK-POINT.
CATCH cx_salv_bs_sc_runtime_info.
ENDTRY.
Big thanks to Sandra Rossi, she gave me the hint to cx_salv_bs_sc_runtime_info.
Related answer: https://stackoverflow.com/a/52834118/633961

Related

Save decoded JSON values in Lua Variables

Following script describes the decoding of a JSON Object, that is received via MQTT. In this case, we shall take following JSON Object as an example:
{"00-06-77-2f-37-94":{"publish_topic":"/stations/test","sample_rate":5000}}
After being received and decoded in the handleOnReceive function, the local function saveTable is called up with the decoded object which looks like:
["00-06-77-2f-37-94"] = {
publish_topic = "/stations/test",
sample_rate = 5000
}
The goal of the saveTable function is to go through the table above and assign the values "/stations/test" and 5000 respectively to the variables pubtop and rate. When I however print each of both variables, nil is returned in both cases.
How can I extract the values of this table and save them in mentioned variables?
If i can only save the values "publish_topic = "/stations/test"" and "sample_rate = 5000" at first, would I need to parse these to get the values above and save them, or is there another way?
local pubtop
local rate
local function saveTable(t)
local conversionTable = {}
for k,v in pairs(t) do
if type(v) == "table" then
conversionTable [k] = string.format("%q: {", k)
printTable(v)
print("}")
else
print(string.format("%q:", k) .. v .. ",")
end
end
pubtop = conversionTable[0]
rate = conversionTable[1]
end
local lua_value
local function handleOnReceive(topic, data, _, _)
print("handleOnReceive: topic '" .. topic .. "' message '" .. data .. "'")
print(data)
lua_value = JSON:decode(data)
saveTable(lua_value)
print(pubtop)
print(rate)
end
client:register('OnReceive', handleOnReceive)
previous question to thread: Decode and Parse JSON to Lua
The function I gave you was to recursively print table contents. It was not ment to be modified to get some specific values.
Your modifications do not make any sense. Why would you store that string in conversionTable[k]? You obviously have no idea what you're doing here. No offense but you should learn some basics befor you continue.
I gave you that function so you can print whatever is the result of your json decode.
If you know you get what you expect there is no point in recursively iterating through that table.
Just do it like that
for k,v in pairs(lua_value) do
print(k)
print(v.publish_topic)
print(v.sample_rate)
end
Now read the Lua reference manual and do some beginners tutorials please.
You're wasting a lot of time and resources if you're trying to implement things like that if you do not know how to access the elements of a table. This is like the most basic and important operation in Lua.

Azure eventhub sending several float array messages in python

I have three float arrays of data (A,B,C) that I want to send it to event hub.
The issue here is that the classes are nested and I can't figure out how to shape the data to be able to send successfully.
Here is the code that I am trying
logger = logging.getLogger("azure")
ADDRESS = ""
USER = ""
KEY = ""
try:
if not ADDRESS:
raise ValueError("No EventHubs URL supplied.")
# Create Event Hubs client
client = EventHubClient(ADDRESS, debug=False, username=USER, password=KEY)
sender = client.add_sender(partition="0")
client.run()
x_value = np.arange(100)
try:
start_time = time.time()
for i in range(100000):
A = np.asarray([1,2,3,4])
B = np.asarray([2,3,4,5])
C = np.asarray([3,4,5,6])
message = [A, B, C]
sender.send(EventData(body = message))
time.sleep(1)
except:
raise
finally:
end_time = time.time()
client.stop()
run_time = end_time - start_time
logger.info("Runtime: {} seconds".format(run_time))
except KeyboardInterrupt:
pass
In this way, I am seeing the error 'ValueBody' object has no attribute 'append'
I think by types, the class encodes the message differently.
Instead of sending a single message with a serial string, I want to send the message in a parallel way, and receive them something like this:
Receive one eventdata with the form of the list [A,B,C]
Receive three eventdata A, B, C separately, like calling three different objects basis.
For the second way, I am not sure if it would work since I am using only one partition and that may have the three eventdata A, B, C mixed when I receive them and I do not want that.
I have confirmed with ms support, the feedback is that: it only works for a list of string or bytes, but does not work for a list of int.
And also you should note that, even it sends a list of string, when receive the list, it will concatenate the all elements of the list to compose a single message. For example, if there is a list ["a","b","c","d"] for sending, when receive it, it will show this "abcd" as a whole string.
I have tested it by myself, the above information is correct.
So for your case, you should consider change the list to a string or json string. when receiving it, you can format the data to what you need.
Hope it helps.

Catch SAPSQL_DATA_LOSS

I want to catch and handle SAPSQL_DATA_LOSS in my ABAP code.
I tried this:
try.
SELECT *
FROM (rtab_name) AS rtab
WHERE (sub_condition)
into table #<sub_result>
.
catch SAPSQL_DATA_LOSS into error.
...
endtry.
But above code is not valid. I get this message:
Type "SAPSQL_DATA_LOSS" is not valid
And I tried this:
catch SYSTEM-EXCEPTIONS SAPSQL_DATA_LOSS = 123.
SELECT *
...
.
endcatch.
if sy-subrc = 123.
...
endif.
But above code gives me:
Instead of "SAPSQL_DATA_LOSS" expected "system-exception" (translated from german to english by me)
How to catch SAPSQL_DATA_LOSS?
This question is not about "why does this exception happen?". This is already solved. My code should handle the exception.
SAPSQL_DATA_LOSS is a runtime error.
As SAPSQL_DATA_LOSS is not a class-based exception, it is not possible to catch it using try catch.
As SAPSQL_DATA_LOSS is not a catchable runtime error, it is not possible to catch it using try catch SYSTEM-EXCEPTIONS.
see the below catchable runtime errors.
https://help.sap.com/doc/abapdocu_751_index_htm/7.51/en-US/abenueb-abfb-sysexc.htm
After some tries I can propose you a possible solution.
This is a workaround:
I don't know if it can be applied to your case, since it needs the select statement to be wrapped into an RFC function module !
The main point is that a short dump (message type X) CAN be handled in RFC calls.
So using an RFC (CALL FUNCTION 'xxxxx' destination 'NONE' for example) and using special exception SYSTEM_FAILURE, the system does not terminate the caller program, but instead it returns a SY-SUBRC > 0 with the Short dump informations in system message fields (SY-MSGxx).
STEPS
Create a Function module (RFC enabled) with your select statement input + the row type of the result table. (All parameters passed by value)
You need this last parameter since generic tables can't be passed in RFC (no "TYPE ANY TABLE" allowed)
FUNCTION Z_DYN_SEL .
*"----------------------------------------------------------------------
*"*"Local interface:
*" IMPORTING
*" VALUE(RTAB_NAME) TYPE TABNAME16
*" VALUE(SUB_CONDITION) TYPE STRING
*" VALUE(RESULT_TYPE) TYPE STRING
*"----------------------------------------------------------------------
* RTAB_NAME: DB Table
* SUB_CONDITION: WHERE Condition
* RESULT_TYPE: The ROW type of the internal table
field-symbols <sub_result> type any table.
* DEFINE LOCAL DYNAMIC TABLE TO STORE THE RESULT
data: lr_res type ref to data.
create data lr_res type standard table of (result_type).
assign lr_res->* to <sub_result>.
* DYNAMIC SELECT
select *
from (rtab_name) as rtab
where (sub_condition)
into table #<sub_result>.
* EXPORT RESULT TO A MEMORY ID, SO IT CAN BE RETRIEVED BY CALLER
export res = <sub_result> to memory id 'RES'.
Main program:
In this caller example some parameters are passed to the RFC.
KTOKD field (should be 4 chars long) is passed with a char10 value (producing your short dump).
If ANY Dump is triggered inside the function, we can now handle it.
If everything went fine, IMPORT result from the EXPORT statement inside the RFC
field-symbols <sub_result> type any table.
data: lr_res type ref to data.
create data lr_res type standard table of KNA1.
assign lr_res->* to <sub_result>.
data lv_msg type char255.
call function 'Z_DYN_SEL' destination 'NONE'
exporting
rtab_name = 'KNA1'
sub_condition = `KTOKD = 'D001xxxxxx'`
result_type = 'KNA1'
exceptions
system_failure = 1 message lv_msg.
if sy-subrc = 0.
import res = <sub_result> from memory id 'RES'.
else.
write: / lv_msg.
write : / sy-msgid, sy-msgno, sy-msgty, sy-msgv1, sy-msgv2, sy-msgv3, sy-msgv4.
endif.
RESULTS
After the RFC call in case of a short dump in the select statement, the program is not terminated and the following pieces of information are available
SY-SUBRC = 1
lv_msg is the error text (Data was lost while copying a value.)
Sy-msgid = 00
Sy-msgno = '341'
Sy-msgty = 'X'
Sy-msgv1 = 'SAPSQL_DATA_LOSS'

EOF Error During Dict Slice

I am trying to compile monthly data in to an existing JSON file that I loaded via import json. Initially, my json data just had one property which is 'name':
json_data['features'][1]['properties']
>>{'name':'John'}
But the end result with the monthly data I want is like this:
json_data['features'][1]['properties']
>>{'name':'John',
'2016-01': {'x1':0, 'x2':0, 'x3':1, 'x4':0},
'2016-02': {'x1':1, 'x2':0, 'x3':1, 'x4':0}, ... }
My monthly data are on separate tsv files. They have this format:
John 0 0 1 0
Jane 1 1 1 0
so I loaded them via import csv and parsed through a list of urls and set about placing them in a collective dictionary like so:
file_strings = ['2016-01.tsv', '2016-02.tsv', ... ]
collective_dict = {}
for i in strings:
with open(i) as f:
tsv_object = csv.reader(f, delimiter='\t')
collective_dict[i[:-4]] = rows[0]:rows[1:5] for rows in tsv_object
I checked how things turned out by slicing collective_dict like so:
collective_dict['2016-01']['John'][0]
>>'0'
Which is correct; it just needs to be cast into an integer.
For my next feat, I attempted to assign all of the monthly data to the respective json members as part of their external properties:
for i in file_strings:
for j in range(len(json_data['features'])):
json_data['features'][j]['properties'][i[:-4]] = {}
json_data['features'][j]['properties'][i[:-4]]['x1'] = int(collective_dict[i[:-4]][json_data['features'][j]['properties']['name']][0])
json_data['features'][j]['properties'][i[:-4]]['x2'] = int(collective_dict[i[:-4]][json_data['features'][j]['properties']['name']][1])
json_data['features'][j]['properties'][i[:-4]]['x3'] = int(collective_dict[i[:-4]][json_data['features'][j]['properties']['name']][2])
json_data['features'][j]['properties'][i[:-4]]['x4'] = int(collective_dict[i[:-4]][json_data['features'][j]['properties']['name']][3])
Here I got an arrow pointing at the last few characters:
Syntax Error: unexpected EOF while parsing
It is a pretty complicated slice, I suppose user error is not to be ruled out. However, I did double and triple check things. I also looked up this error. It seems to come up with input() related calls. I'm left a bit confused, I don't see how I made a mistake (although I'm already mentally prepared to accept that).
My only guess was that something somewhere was not a string. When I checked collective_dict and json_data, everything that was supposed to be a string was a string ('John', 'Jane' et all). So, I guess it's something else.
I made the problem as simple as I could while keeping the original structure of the data and for loops and so forth. I'm using Python 3.6.
Question
Why am I getting the EOF error? How can I build my external properties data without encountering such an error?
Here I have rewritten your last code block to:
for i in file_strings:
file_name = i[:-4]
for j in range(len(json_data['features'])):
name = json_data['features'][j]['properties']['name']
file_dict = json_data['features'][j]['properties'][file_name] = {}
for x in range(4):
x_string = 'x{}'.format(x+1)
file_dict[x_string] = int(collective_dict[file_name][name][x])
from:
for i in file_strings:
for j in range(len(json_data['features'])):
json_data['features'][j]['properties'][i[:-4]] = {}
json_data['features'][j]['properties'][i[:-4]]['x1'] = int(collective_dict[i[:-4]][json_data['features'][j]['properties']['name']][0])
json_data['features'][j]['properties'][i[:-4]]['x2'] = int(collective_dict[i[:-4]][json_data['features'][j]['properties']['name']][1])
json_data['features'][j]['properties'][i[:-4]]['x3'] = int(collective_dict[i[:-4]][json_data['features'][j]['properties']['name']][2])
json_data['features'][j]['properties'][i[:-4]]['x4'] = int(collective_dict[i[:-4]][json_data['features'][j]['properties']['name']][3])
That is just to make it a bit more readable, but that shouldn't change anything.
A thing I noticed in your other part of code is the following:
collective_dict[i[:-4]] = rows[0]:rows[1:5] for rows in tsv_object
The thing I refer to is the = rows[0]:rows[1:5] for rows in tsv_object part. In my IDE, that does not work, and I'm not sure if that is a typo in your question or of that is actually in your code, but I imagine you want it to actually be
collective_dict[i[:-4]] = {rows[0]:rows[1:5] for rows in tsv_object}
or something like that. I'm not sure if that could confuse the parser think that there is an error at the end of the file.
The ValueError: Invalid literal for int()
If your tsv-data is
John 0 0 1 0
Jane 1 1 1 0
Then it should be no problem to do int() of the string value. E.g.: int('42') will become an int with value 42. However, if you have an error in one, or several, lines of your files, then use something like this block of code to figure out which file and line it is:
file_strings = ['2016-01.tsv', '2016-02.tsv', ... ]
collective_dict = {}
for file_name in file_strings:
print('Reading {}'.format(file_name))
with open(file_name) as f:
tsv_object = csv.reader(f, delimiter='\t')
for line_no, (name, *x_values) in enumerate(tsv_object):
if len(x_values) != 4:
print('On line {}, there is only {} values!'.format(line_no, len(x_values)))
try:
intx = [int(x) for x in x_values]
except ValueError as e:
# Catch "Invalid literal for int()"
print('Line {}: {}'.format(line_no, e))

How to convert generic exception into BAPIRET2 message?

I have several custom exception classes that were created "With Message Class". Since I can't directly get a message from them, I want to create a utility method that returns a BAPIRET2 from a given exception based on the values in IF_T100_MESSAGE~T100KEY. However, I can't provide that method with a generic CX_ROOT importing parameter as this class is not message-enabled. I also can't create a generic message-enabled exception class as new classes have to inherit from one of CX_STATIC_CHECK, CX_DYNAMIC_CHECK, or CX_NOCHECK.
How can I then retrieve the message details from an unspecified exception? Should I create a method that receives a CX_ROOT and then does up to three calls to methods with an import typed to each of the three possible subclasses? Or are there better alternatives?
You could prepare a type descriptor of the interface (once):
DATA: lr_t100_descr TYPE REF TO cl_abap_intfdescr.
lr_t100_descr ?= cl_abap_typedescr=>describe_by_name( 'IF_T100_MESSAGE' ).
and then examine each exception as it comes your way:
DATA: lr_t100_exception TYPE REF TO if_t100_message.
IF lr_t100_descr->applies_to( ir_any_exception ) = abap_true.
lr_t100_exception ?= ir_any_exception.
" ...
ENDIF.
You could use the message collector object, so for example
DATA:
excp type ref to CX_ROOT,
bapi_messages type BAPIRETTAB,
message_collector type ref to IF_RECA_MESSAGE_LIST.
FIELD_SYMBOLS:
<bapi_message> TYPE BAPIRET2.
message_collector = cf_reca_message_list=>create( ).
TRY.
" some code which may cause and exception
CATCH cx_root into excp.
message_collector->add_from_exxeption( io_exception = excp).
ENDTRY.
bapi_messages = message_collector->get_list_as_bapiret( ).
LOOP AT bapi_messages ASSIGNING <bapi_message>.
" write out message
ENDLOOP.
It is well worth checking out the message collector object.
For example
http://wiki.scn.sap.com/wiki/display/profile/2007/07/09/Message+Handling+-+Finding+the+Needle+in+the+Haystack
For a logging class I use something like this:
METHOD add_message_exception.
DATA:
lr_type TYPE REF TO cl_abap_typedescr,
lr_class TYPE REF TO cl_abap_classdescr,
lr_intf TYPE REF TO cl_abap_intfdescr,
l_bapiret2 TYPE bapiret2,
lr_msg TYPE REF TO if_t100_message.
CHECK ir_exception IS NOT INITIAL.
l_bapiret2-type = i_type.
"Test for T100KEY interface
cl_abap_classdescr=>describe_by_object_ref(
EXPORTING
p_object_ref = ir_exception
RECEIVING
p_descr_ref = lr_type
EXCEPTIONS
reference_is_initial = 1
OTHERS = 2 ).
TRY.
lr_class ?= lr_type.
IF sy-subrc = 0.
lr_class->get_interface_type(
EXPORTING
p_name = 'IF_T100_MESSAGE'
RECEIVING
p_descr_ref = lr_intf
EXCEPTIONS
interface_not_found = 1
OTHERS = 2 ).
IF sy-subrc = 0.
lr_msg ?= ir_exception. "Cast to interface
l_bapiret2-id = lr_msg->t100key-msgid.
l_bapiret2-number = lr_msg->t100key-msgno.
cl_message_helper=>set_msg_vars_for_if_t100_msg( text = lr_msg ).
l_bapiret2-message_v1 = sy-msgv1.
l_bapiret2-message_v2 = sy-msgv2.
l_bapiret2-message_v3 = sy-msgv3.
l_bapiret2-message_v4 = sy-msgv4.
l_bapiret2-message = me->get_msg(
i_msgid = l_bapiret2-id
i_msgno = l_bapiret2-number ).
ENDIF.
ENDIF.
CATCH cx_root.
"Pokémon exception handling
ENDTRY.
"No T100KEY Interface available
IF lr_msg IS INITIAL.
l_bapiret2-message = ir_exception->if_message~get_text( ).
l_bapiret2-message_v1 = sy-msgv1.
l_bapiret2-message_v2 = sy-msgv2.
l_bapiret2-message_v3 = sy-msgv3.
l_bapiret2-message_v4 = sy-msgv4.
ENDIF.
ENDMETHOD.
Hope this helps as I struggled with the same problem. Maybe there is some adjustment needed, but I think you get the basic idea. This method can handle
I might be missing something, but can't you just use IF_MESSAGE~GET_TEXT which is present on CX_ROOT ?
Otherwise, I would make it the responsibility of the custom exception class to have a method that can return a proper message ( it might rely on the utility method you are planning on ).