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 Antworten
Sehr guter Artikel, weiter so! 🙂
Liebe Gruesse an alle aus Perth,
Robert
@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.
Hmm, ist es irgendwann geplant, dass diese Json Funktionen auch auf dem Server laufen?
@Goecom_RI
Im SOA-Service können JSON-Funktionen verwendet werden, in durch RmtCall() gestarteten Prozeduren geht dies nicht.
Hallo,
besteht die Möglichkeit JSON Funktionen auch auf dem Server laufen zu lassen?