[JavaScript] JSON-Datei per fetch( ) oder FilePicker laden

Im Beispiel wird eine JSON-Datei eingelesen. Hierzu gibt es zwei Vorgehensweisen:

  1. Zuerst wird versucht die Datei per fetch() zu laden. Herbei kann es zum Fehler „Failed to fetch“ kommen. Die Fehlerursache ist häufig, dass der Browser den Zugriff auf lokale Dateien blockiert oder die Anfrage wegen einer Cross‑Origin‑Policy ablehnt.
  2. Falls fetch( ) fehlschlägt, kann per OpenDialog (FilePicker) lokal eine Datei ausgewählt werden.
<!doctype html>
<html lang="de">
  <head>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="width=device-width,initial-scale=1" />
    <title>JSON-Datei per URL oder lokal laden</title>
    <style>
      #filePicker {
        margin: 8px 0;
      }
    </style>
  </head>
  <body>
    <div id="container">
      <div id="statusText">JSON-Datei laden</div>
      <br />
      <div
        id="filePicker"
        style="display: none">
        <label>JSON-Datei auswählen:</label>
        <input
          type="file"
          id="fileOpenDialog"
          accept=".json" />
      </div>
      <br />
      <div id="fileContentText">-</div>
    </div>

    <script>
      // Datei mit Beispieldaten (URL)
      //const JSON_SRC = 'https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json';
      // Datei mit Beispieldaten (lokal)
      const JSON_SRC = "MyLocalFile.json";

      // Beispielhafte Weiterverarbeitung der JSON-Daten
      function processJSON(json) {
        if (json != null) {
          document.getElementById("statusText").textContent =
            "JSON-Datei wurde geladen und wird nun weiter verarbeitet.";

          const text = JSON.stringify(json, null, 2);

          document.getElementById("fileContentText").textContent = text;
        }
      }

      document.addEventListener("DOMContentLoaded", () => {
        const txtStatusText = document.getElementById("statusText");

        // erst automatischer Ladevorgang per fetch( ) probieren,
        // bei Fehler Fallback: manueller Datei-Upload
        fetch(JSON_SRC)
          .then((resp) => {
            if (!resp.ok) throw new Error("HTTP " + resp.status);

            // JSON zu Objekt parsen
            return resp.json();
          })
          .then((json) => {
            // ggf. statusText ausblenden
            // txtStatusText.style.display = 'none';

            // JSON verarbeiten
            processJSON(json);
          })
          .catch((err) => {
            txtStatusText.textContent =
              "Fehler beim automatischen Laden: " + err.message;

            // wenn Datei nicht per fetch( ) geladen werden kann,
            // FilePicker anzeigen und dann manuelle Angabe der Datei
            document.getElementById("filePicker").style.display = "block";
          });

        const fileInput = document.getElementById("fileOpenDialog");
        // manueller Datei‑Upload

        fileInput.addEventListener("change", (ev) => {
          const f = ev.target.files && ev.target.files[0];

          if (!f) return;

          const reader = new FileReader();

          reader.onload = function (e) {
            try {
              // JSON zu Objekt parsen
              const json = JSON.parse(e.target.result);

              // ggf. statusText ausblenden
              //document.getElementById('statusText').style.display = 'none';

              // JSON verarbeiten
              processJSON(json);
            } catch (ex) {
              document.getElementById("statusText").textContent =
                "Fehler: Ungültige JSON-Datei.";
            }
          };

          reader.onerror = function () {
            document.getElementById("statusText").textContent =
              "Fehler beim Lesen der Datei.";
          };

          reader.readAsText(f);
        });
      });
    </script>
  </body>
</html>

Links

[ABAP] JSON -> ABAP (xco_cp_json)

TYPES: BEGIN OF ty_s_person,
         name  TYPE string,
         title TYPE string,
         age   TYPE i,
       END OF ty_s_person.

DATA(lv_json) = |\{"name":"Horst","title":"Herr","age":30\}|.

DATA(lv_persons) = VALUE ty_s_person( ).

TRY.
    xco_cp_json=>data->from_string( lv_json
                                  )->apply( VALUE #( ( xco_cp_json=>transformation->boolean_to_abap_bool )
                                                     ( xco_cp_json=>transformation->pascal_case_to_underscore ) )
                                  )->write_to( REF #( lv_persons ) ).

    cl_demo_output=>write_data( lv_json ).
    cl_demo_output=>write_data( lv_persons ).

* HTML-Code vom Demo-Output holen
    DATA(lv_html) = cl_demo_output=>get( ).

* Daten im Inline-Browser im SAP-Fenster anzeigen
    cl_abap_browser=>show_html( EXPORTING title       = 'JSON'
                                          html_string = lv_html
                                          container   = cl_gui_container=>default_screen ).

* cl_gui_container=>default_screen erzwingen
    WRITE: space.
  CATCH cx_root.
ENDTRY.

Links

[ABAP] ABAP -> JSON (xco_cp_json)

TYPES: BEGIN OF ty_s_person,
         name  TYPE string,
         title TYPE string,
         age   TYPE i,
       END OF ty_s_person.

TYPES: ty_t_person TYPE STANDARD TABLE OF ty_s_person WITH DEFAULT KEY.

DATA(it_persons) = VALUE ty_t_person( ( name = 'Horst' title = 'Herr' age = 30 )
                                      ( name = 'Jutta' title = 'Frau' age = 35 )
                                      ( name = 'Ingo'  title = 'Herr' age = 31 ) ).

TRY.
* ABAP -> JSON
    DATA(lv_json) = xco_cp_json=>data->from_abap( it_persons )->to_string( ).

    cl_demo_output=>write_data( it_persons ).
    cl_demo_output=>write_data( lv_json ).

* HTML-Code vom Demo-Output holen
    DATA(lv_html) = cl_demo_output=>get( ).

* Daten im Inline-Browser im SAP-Fenster anzeigen
    cl_abap_browser=>show_html( EXPORTING title       = 'JSON'
                                          html_string = lv_html
                                          container   = cl_gui_container=>default_screen ).

* cl_gui_container=>default_screen erzwingen
    WRITE: space.
  CATCH cx_root.
ENDTRY.

Links

[ABAP] JSON parsen und in Node-Table wandeln

* Quelle: https://github.com/i042416/KnowlegeRepository/blob/master/ABAP/SmallApp/011_ZCL_JERRY_TOOL.abap
CLASS lcl_json_parser DEFINITION.
  PUBLIC SECTION.

    TYPES: BEGIN OF ty_node,
             type      TYPE string,
             prefix    TYPE string,
             name      TYPE string,
             nsuri     TYPE string,
             value     TYPE string,
             value_raw TYPE xstring,
           END OF ty_node.

    TYPES: ty_it_node TYPE STANDARD TABLE OF ty_node WITH DEFAULT KEY.

    CONSTANTS: co_open_element TYPE string VALUE 'OPEN'.
    CONSTANTS: co_close_element TYPE string VALUE 'CLOSE'.
    CONSTANTS: co_attribute TYPE string VALUE 'ATTRIBUTE'.
    CONSTANTS: co_value TYPE string VALUE 'VALUE'.

    CLASS-METHODS: convert_json_to_node_table
      IMPORTING
                iv_json            TYPE string
      RETURNING VALUE(rv_it_nodes) TYPE ty_it_node
      RAISING
                cx_sxml_parse_error.
ENDCLASS.

CLASS lcl_json_parser IMPLEMENTATION.
  METHOD convert_json_to_node_table.

* JSON nach XSTRING (UTF-8) konvertieren
    DATA(o_json) = cl_abap_codepage=>convert_to( iv_json ).
* XSTRING einlesen und nach XML parsen
    DATA(o_reader) = cl_sxml_string_reader=>create( o_json ).

    TRY.
* erste Node holen
        DATA(o_node) = o_reader->read_next_node( ).

* solange es Nodes gibt
        WHILE o_node IS BOUND.
* Node-Typen prüfen
          CASE o_node->type.
* Öffnen-Node
            WHEN if_sxml_node=>co_nt_element_open.
              DATA(op) = CAST if_sxml_open_element( o_node ).
              APPEND VALUE #( type   = co_open_element
                              prefix = op->prefix
                              name   = op->qname-name
                              nsuri  = op->qname-namespace ) TO rv_it_nodes.

* Attribute
              LOOP AT op->get_attributes( ) ASSIGNING FIELD-SYMBOL(<a>).
                APPEND VALUE #( type      = co_attribute
                                prefix    = <a>->prefix
                                name      = <a>->qname-name
                                nsuri     = <a>->qname-namespace
                                value     = COND #( WHEN <a>->value_type = if_sxml_value=>co_vt_text THEN <a>->get_value( ) )
                                value_raw = COND #( WHEN <a>->value_type = if_sxml_value=>co_vt_raw THEN <a>->get_value_raw( ) ) ) TO rv_it_nodes.
              ENDLOOP.
* Schließen-Node
            WHEN if_sxml_node=>co_nt_element_close.
              DATA(cl) = CAST if_sxml_close_element( o_node ).
              APPEND VALUE #( type   = co_close_element
                              prefix = cl->prefix
                              name   = cl->qname-name
                              nsuri  = cl->qname-namespace ) TO rv_it_nodes.

* Wert-Node
            WHEN if_sxml_node=>co_nt_value.
              DATA(val) = CAST if_sxml_value_node( o_node ).
              APPEND VALUE #( type      = co_value
                              value     = COND #( WHEN val->value_type = if_sxml_value=>co_vt_text THEN val->get_value( ) )
                              value_raw = COND #( WHEN val->value_type = if_sxml_value=>co_vt_raw THEN val->get_value_raw( ) ) ) TO rv_it_nodes.
* Andere Nodetypen
            WHEN OTHERS.

          ENDCASE.

* nächste Node
          o_node = o_reader->read_next_node( ).
        ENDWHILE.

      CATCH cx_root INTO DATA(e_txt).
        RAISE EXCEPTION TYPE cx_sxml_parse_error
          EXPORTING
            error_text = e_txt->get_text( ).
    ENDTRY.
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  TRY.
* JSON-Beispiele
*      DATA(lv_json) = CONV string( '{"success":"true","msg":"Ok.","matnr":"0000001234"}' ).
      DATA(lv_json) = CONV string( '{"VALUES":[{"NAME":"Horst","TITLE":"Herr","AGE":30},{"NAME":"Jutta","TITLE":"Frau","AGE":35},{"NAME":"Ingo","TITLE":"Herr","AGE":31}]}' ).

* JSON -> Nodetable
      DATA(it_node_values) = lcl_json_parser=>convert_json_to_node_table( lv_json ).

* Datenausgabe
      cl_demo_output=>write_data( lv_json ).
      cl_demo_output=>write_data( it_node_values ).
      cl_demo_output=>display( ).
    CATCH cx_root INTO DATA(e_txt).
      WRITE: / e_txt->get_text( ).
  ENDTRY.

[ABAP] Einfacher REST-Service (ohne OData)

Piyush Philip auf sap.com hat eine simple Möglichkeit beschrieben, wie man in ABAP einen REST-Service mit den entsprechenden CRUD-Methoden implementieren kann. Dazu ist im Wesentlichen nur eine eigene Handler-Klasse zu implementieren, die von IF_HTTP_EXTENSION erbt. Diese Handler-Klasse wird von einem Service des ICF gerufen.

  1. SE80 -> Neue Klasse anlegen
  2. In der Klasse im Reiter Interfaces: vom Interface IF_HTTP_EXTENSION erben
  3. die Interface-Methode IF_HTTP_EXTENSION~HANDLE_REQUEST implementieren (implementiert nur REST, kein OData-Sevice!)
    METHOD if_http_extension~handle_request.
    
    * Typen
        TYPES: BEGIN OF ty_json_request,
                 param1 TYPE char64,
                 param2 TYPE char64,
               END OF ty_json_request.
    
        TYPES: BEGIN OF ty_json_response,
                 success   TYPE string,
                 msg       TYPE string,
                 operation TYPE string,
               END OF ty_json_response.
    
    * Variablen
        DATA: rv_response TYPE ty_json_response.
        DATA: it_path_info TYPE stringtab.
        DATA: it_query_strings TYPE stringtab.
    
    * Header Fields auslesen
        DATA: it_header_fields TYPE tihttpnvp.
        server->request->get_header_fields( CHANGING fields = it_header_fields ).
    
    * Request Methode (POST, GET, PUT, DELETE ...)
        DATA(lv_request_method) = server->request->get_header_field( name = '~request_method' ).
    
    * URL Pathinfo
        DATA(lv_path_info) = server->request->get_header_field( name = '~path_info' ).
    * erstes '/' entfernen
        SHIFT lv_path_info LEFT BY 1 PLACES.
        SPLIT lv_path_info AT '/' INTO TABLE it_path_info.
    
    * URL Parameter (Query Strings) für Auswertung
        DATA(lv_query_string) = server->request->get_header_field( name = '~query_string' ).
        SPLIT lv_query_string AT '&' INTO TABLE it_query_strings.
    
    * Request Methoden behandeln
        CASE lv_request_method.
    * C (Create)
          WHEN 'POST'.
    * Beispiel: per Request übermittelte JSON-Daten auslesen und behandeln
    * Content-Typ auslesen
            DATA(lv_request_content_type) = server->request->get_content_type( ).
    
    * Wenn Content-Typ JSON
            IF lv_request_content_type CP if_rest_media_type=>gc_appl_json.
    * JSON-Daten auslesen und nach ABAP-Typ konvertieren -> Parameternamen müssen übereinstimmen, sonst HTTP 500!
              DATA(lv_request_cdata) = server->request->get_cdata( ).
    
              DATA: lv_request_abap TYPE ty_json_request.
    
              TRY.
                  /ui2/cl_json=>deserialize( EXPORTING json = lv_request_cdata
                                                       pretty_name = /ui2/cl_json=>pretty_mode-camel_case
                                             CHANGING  data = lv_request_abap ).
    
                CATCH cx_root INTO DATA(e_txt).
                  MESSAGE e_txt->get_text( ) TYPE 'S' DISPLAY LIKE 'E'.
              ENDTRY.
    
              rv_response = VALUE #( success   = 'true'
                                     msg       = lv_request_abap-param1 && ' | ' && lv_request_abap-param2
                                     operation = lv_request_method ).
            ELSE.
              rv_response = VALUE #( success   = 'false'
                                     msg       = 'Request content type must be application/json'
                                     operation = lv_request_method ).
            ENDIF.
    * R (Read)
          WHEN 'GET'.
            DATA(lv_param) = server->request->get_form_field( 'param' ).
    
            rv_response = VALUE #( success   = 'true'
                                   msg       = lv_param
                                   operation = lv_request_method ).
    * U (Update)
          WHEN 'PUT'.
            rv_response = VALUE #( success   = 'true'
                                   msg       = ''
                                   operation = lv_request_method ).
    * D (Delete)
          WHEN 'DELETE'.
            rv_response = VALUE #( success   = 'true'
                                   msg       = ''
                                   operation = lv_request_method ).
    
    * unerlaubte Methoden abfangen
          WHEN OTHERS.
    * Fehlercode 405 zurückgeben
            server->response->set_status( code   = cl_rest_status_code=>gc_client_error_meth_not_allwd
                                          reason = 'method not allowed' ).
    
            server->response->set_content_type( if_rest_media_type=>gc_appl_json ).
    
            server->response->set_header_field( name  = if_http_header_fields=>allow
                                                value = 'POST,GET,PUT,DELETE' ).
    
            rv_response = VALUE #( success   = 'false'
                                   msg       = 'error'
                                   operation = lv_request_method ).
        ENDCASE.
    
    * wenn erfolgreich, dann Wert zurückgeben
        IF rv_response-success = 'true'.
    * HTTP-Status 200
          server->response->set_status( code   = cl_rest_status_code=>gc_success_ok
                                        reason = 'Ok.' ).
    
    * application/json
          server->response->set_content_type( if_rest_media_type=>gc_appl_json ).
    
    * Service-Response als JSON zurückgeben
          server->response->set_cdata( data = /ui2/cl_json=>serialize( data        = rv_response
                                                                       pretty_name = /ui2/cl_json=>pretty_mode-camel_case ) ).
    
        ENDIF.
    
    ENDMETHOD.
    
  4. SICF -> neue ICF-Node (Sub-Element) unterhalb default_host -> sap -> bc als neuen unabhängigen Service anlegen
  5. in der Handlerliste des Service die erstellte Klasse als „Handler“ angeben
  6. auf die neue ICF-Node rechtsklicken -> Service aktivieren
  7. auf die neue ICF-Node rechtsklicken -> Service testen
  8. URL aus Browserfenster kopieren (http://domain.de/sap/bc/servicename?sap-client=900)
  9. im Browser mir einem Rest-Plugin (FireFox-Prugin: Rested) testen

Links

[SAP Gateway] Einfacher REST-Webservice / CRUD-Applikation

Links

[ABAP] Struktur -> JSON

Variante 1 (/ui2/cl_json)

* ABAP-Typ
TYPES: BEGIN OF ty_struct,
         name TYPE string,
         age  TYPE i,
         size TYPE f,
       END OF ty_struct.

* Test-Daten
DATA(lv_struc) = VALUE ty_struct( name = 'Udo' age = 25 size = '1.5' ).

* Struct -> JSON
* {"name":"Udo","age":25,"size":1.5000000000000000E+00}
DATA(lv_json_str) = /ui2/cl_json=>serialize( data        = lv_struc
                                             pretty_name = /ui2/cl_json=>pretty_mode-camel_case ).

WRITE: / lv_json_str.

Variante 2 (/ui2/cl_abap2json)

* ABAP-Typ
TYPES: BEGIN OF ty_struct,
         name TYPE string,
         age  TYPE i,
         size TYPE f,
       END OF ty_struct.

* Test-Daten
DATA(lv_struc) = VALUE ty_struct( name = 'Udo' age = 25 size = '1.5' ).

* Struct -> JSON
DATA(o_conv) = NEW /ui2/cl_abap2json( ).
DATA(lv_json_str) = o_conv->struc2json( iv_struc = lv_struc ).

WRITE: / lv_json_str.