Programmierung

JSON

Bei der Entwicklung von Schnittstellen zum Datenaustausch mit anderen Softwarekomponenten, stellt sich oft die gleiche Frage:

In welchem Format sind die Daten am besten verpackt?

Eine mögliche Antwort, die ich in diesem Artikel näher beleuchten werde, lautet: JSON.


Anforderungen

Das beste Format ist das, mit dem möglichst viele Anforderungen an die Kommunikation abgedeckt werden. Dies können zum Beispiel sein:

  • schnell (geringer Overhead, geringe CPU-Last)
  • einfach zu verstehen (menschenlesbar)
  • einfach zu implementieren
  • hohe Auf- und Abwärstskompatibilität der Schnittstelle
  • hohe Verfügbarkeit auf anderen Entwicklungsplatformen
  • einfache Validierung der empfangenen Daten

Format

JSON (JavaScript Object Notation; engl. für JavaScript-Objekt-Schreibweise) ist ein Format, dessen Ursprünge im Web, genauer gesagt in JavaScript liegen und das mittlerweile weit verbreitet ist. Es deckt alle der erwähnten Anforderungen ab und eignet sich daher besonders gut zum Datenaustausch.

  • Es ist schnell, da es einen sehr geringen Overhead hat, die übertragenen Daten zum größten Teil also aus Nutzdaten bestehen. Außerdem ist es durch seine Kompaktheit sehr einfach zu interpretieren (zu parsen) und belastet die CPU somit nur wenig.
  • JSON ist einfach zu verstehen, da es text-basiert und von Menschen lesbar ist und eine leicht verständliche Syntax verwendet.
  • JSON ist einfach zu implementieren, da das Format auf wenigen, simplen Datentypen basiert.
  • Die Schnittstelle kann auf- und abwärtskompatibel gehalten werden, da die übertragenen Nachrichten einfach erweitert und die erwarteten Daten einfach angepasst werden können.
  • Die Verfügbarkeit von JSON erstreckt sich über eine Vielzahl von Entwicklungsumgebungen, für die Mechanismen zur Verarbeitung von JSON vorhanden sind.
  • Die Validierung der empfangenen Daten wird zum Teil schon durch die Interpretierung übernommen. Wird eine Zeichenkette erwartet, muss eine Zeichenkette vorhanden sein. Mittlerweile gibt es mit JSON-Schemas auch eine Möglichkeit JSON-Daten detaillierter auf ihre Korrektheit zu prüfen, ähnlich wie XML-Dokumente mit XML-Schemas.
Beispiel
{
  "object":{
    "one":"1",
    "two":2
  },
  "array":[
    "1",
    2
  ],
  "number":1234567890,
  "boolean":true,
  "null":null
}

In diesem Beispiel sind bereits alle Datentypen die in JSON Verwendung finden vorhanden:

  • Durch { und } wird ein Objekt eingeschlossen. Ein Objekt ist eine Liste von eindeutig benannten, durch Kommata getrennten, Unterelementen.
  • Durch [ und ] wird ein Array eingeschlossen. Ein Array ist eine Liste von, durch Kommata getrennten, Unterelementen.
  • Durch “ wird eine Zeichenkette eingeschlossen.
  • Die Ziffern 0 bis 9 beschreiben eine Zahl.
  • Die Schlüsselwörter „true“ und „false“ stellen boolesche Werte da.
  • Ein Null-Wert wird mit dem Schlüsselwort „null“ ausgedrückt.

Eine ausführliche Beschreibung des Formats finden Sie unter www.json.org.
In CONZEPT 16 gestaltet sich die Verarbeitung von JSON-Daten genauso einfach wie die XML Verarbeitung. Über zwei Funktionen können JSON-Daten geschrieben beziehungsweise gelesen werden:

Schreiben/Speichern

Mit der Funktion JSONSave() können in einer Cte-Struktur vorliegende Daten im JSON-Format gespeichert werden.

JSONSave(
  CteNodeJSON       : handle;  // Cte-Struktur mit JSON-Daten
  File              : alpha;   // Datei für JSON-Daten
  opt Options       : int;     // Optionen
  opt Mem           : handle;  // Speicherblock für JSON-Daten
  opt CharsetTarget : int;     // Zielzeichensatz
) : int                        // Fehler

Die JSON-Daten werden über die Eigenschaften der CteNode-Objekte definiert:

  • Der Name eines Elements in einem Objekt wird über die Eigenschaft spName bestimmt.
  • Der Datentyp des Elements wird über die Eigenschaft spID definiert:
    • _JSONNodeArray – Array
    • _JSONNodeObject – Objekt
    • _JSONNodeString – Zeichenkette
    • _JSONNodeNumber – Zahl
    • _JSONNodeBoolean – boolescher Wert
    • _JSONNodeNull – Null-Wert
  • Über die Eigenschaften spValueAlpha, spValueInt, spValueBigInt, spValueFloat, spValueDecimal, spValueLogic kann der Wert eines Elementes gesetzt und abgefragt werden.
  • Der dazugehörige Typ wird in der Eigenschaft spType abgelegt.

Die Unterelemente von Arrays und Objekten werden über die Kindknoten eines CteNode-Objektes abgebildet.
Wird die Funktion erfolgreich durchgeführt, werden die Daten im JSON-Format in die Datei oder den Speicherblock geschrieben und der Rückgabewert _ErrOK zurückgeliefert.

Beispiel

Die obigen JSON-Daten lassen sich wie folgend generieren:

sub JSONSaveToFile
()

  local
  {
    tCteNodeJSON       : handle;
    tCteNodeJSONItem   : handle;
    tErr               : int;
  }

{
  // XML-Dokument als Cte-Knoten anlegen
  tCteNodeJSON # CteOpen(_CteNode);

  // Cte-Knoten als JSON-Objekt deklarieren
  tCteNodeJSON->spID # _JSONNodeObject;
  // Object
  tCteNodeJSONItem # tCteNodeJSON->CteInsertNode('object',
    _JSONNodeObject, NULL);
  // String in Object
  tCteNodeJSONItem->CteInsertNode('one',
    _JSONNodeString, '1');
  // Number in Object
  tCteNodeJSONItem->CteInsertNode('two',
    _JSONNodeNumber, 2);
  // Array
  tCteNodeJSONItem # tCteNodeJSON->CteInsertNode('array',
    _JSONNodeArray, NULL);
  // String in Array
  tCteNodeJSONItem->CteInsertNode('',
    _JSONNodeString, '1');
  // Number in Array
  tCteNodeJSONItem->CteInsertNode('',
    _JSONNodeNumber, 2);
  // Number
  tCteNodeJSON->CteInsertNode('number',
    _JSONNodeNumber, 1234567890);
  // Boolean
  tCteNodeJSON->CteInsertNode('boolean',
    _JSONNodeBoolean, true);
  // Null
  tCteNodeJSON->CteInsertNode('null',
    _JSONNodeNull, null);

  tErr # tCteNodeJSON->JSONSave('C:\JSON-data.txt');

  // Cte-Knoten leeren freigeben
  tCteNodeJSON->CteClear(true);
  tCteNodeJSON->CteClose();

  // Fehler bei JSONSave()
  if (tErr != _ErrOK)
  {
    // ...
  }
}

Lesen/Laden

Mit der Funktion JSONLoad() können Daten im JSON-Format in eine Cte-Struktur geladen werden.

JSONLoad(
  CteNodeJSON : handle;  // CteNode-Objekt für JSON-Daten
  File        : alpha;   // JSON-Daten als Datei
  opt Options : int;     // Optionen
  opt Mem     : handle;  // JSON-Daten als Speicherblock
) : int;                 // Fehlerposition

Wird die Funktion erfolgreich durchgeführt, werden die JSON-Daten in das CteNode-Objekt geladen und der Rückgabewert _ErrOK zurückgeliefert. Ist ein Fehler aufgetreten, liefert die Funktion die Position des Fehlers zurück.

Beispiel

JSON-Daten können wie folgend gelesen werden:

sub JSONLoadFromFile
()

  local
  {
    tCteNodeJSON : handle;
    tErr         : int;
  }

{
  tCteNodeJSON # CteOpen(_CteNode);

  tErr # tCteNodeJSON->JSONLoad('C:\JSON-data.txt');
  if (tErr = _ErrOK)
  {
    // CteNode-Objekte verarbeiten
    // ...
  }

  tCteNodeJSON->CteClear(true);
  tCteNodeJSON->CteClose();

  // Fehler bei JSONLoad()
  if (tErr > _ErrOK)
  {
    WinDialogBox(0,
      'Fehler beim Laden von JSON-Daten',
      'Fehlerposition: ' + CnvAI(tErr),
      _WinIcoError,
      _WinDialogOK,
      1
    );
  }
}

Fazit

Das JSON-Format findet dank seiner kompakten Form und seinem hohen Verbreitungsgrad besonders bei Webservices, Web-Applikationen und im SOA-Bereich viele Anwendungsfälle. Es leistet aber auch überall dort gute Dienste, wo es auf den schonenden Umgang mit Ressourcen, wie Speicher und CPU-Last ankommt.

Haben Sie schon Erfahrung mit JSON gesammelt? Wenn ja, welche und in welchem Bereich?

5 Kommentare

5 Kommentare “JSON”

  1. @Goecom_RI
    Eine per RmtCall() gestartete Prozedur läuft innerhalb des Server-Datenbankprozesses und kann sich dort negativ auswirken und die Stabilität des Servers beeinträchtigen.

    Deshalb sind Verarbeitungen mit Daten außerhalb der Datenbank (z.B. Operationen mit externen Dateien) in RmtCall()-Prozeduren grundsätzlich nicht empfehlenswert.

    Besser ist es, auf dem Datenbankserver einen SOA-Service zu installieren, der diese Operationen durchführt. Auch in diesem Szenario kann der Client sehr einfach eine Remote-Prozedur im SOA-Service starten.

Kommentar abgeben