Hallo zusammen,
nachdem ich gestern in die Verlegenheit kam mit dem - in Delphi integrierten - JSON Objects Framework (System.JSON) zu arbeiten, habe ich mir eine kleine Sammlung an Helper Klassen- und Funktionen geschrieben, um einige Schwächen auszubessern.
Generell kann man gut mit dem Framework arbeiten, allerdings haben mich ein paar Dinge enorm gestört:
- Man muss sehr viel casten und bei praktisch jeder Operation Typ-Checks mit dem is
Operator durchführen
- Geht dann irgendwas schief, wird eine nichtssagende Exception ".. kann nicht in .. konvertiert werden" geschmissen, sofern man nicht selbstständig nachbessert
- Beim Generieren von JSON, ist die resultierende textuale Darstellung immer in unformatierter Kompaktform (sprich: unlesbar)
Hierfür habe ich die Helferklassen
IJSONArrayReader
bzw.
IJSONObjectReader
implementiert. Die Klassen sind Wrapper für
TJSONArray
bzw.
TJSONObject
und implementieren typisierte Read-Operationen auf dem darunterliegenden Objekt. Hierbei wird automatisch der JSON Path mitgeschleppt, um Diesen im Falle einer Typ-
Exception in der Meldung mit anzeigen zu können. Zusätzlich zu den trivialen standard JSON-Datentypen habe ich generische Methoden zum Lesen von Enums und Sets implementiert. Sämtlichen Read-Operationen kann zudem ein optionaler Default-Wert mitgegeben werden.
Komplexe JSON-Datentypen wie Array und Object können ebenfalls gelesen werden, wobei hier eine neue Instanz des entsprechenden Readers zurückgegeben wird, damit man die Aufrufe bei Bedarf auch chainen kann.
Analog zu den Reader-Klassen gibt
IJSONArrayWriter
und
IJSONObjectWriter
mit inverser Funktionalität und einigen Komfort-Funktionen wie konditionales Schreiben von Arrays oder Objekten (ich befand mich öfters in der Situation entweder ein Array schreiben zu wollen, oder aber NULL, wenn das Array keine Elemente enthielt).
Um das Problem der Formatierung zu lösen, gibt es abschließend die
TJSONFormatter.Format
Methode, welche auch PrettyPrint-Formatting mit einstellbarer Einrückung unterstüzt.
Die aktuellste Version gibt es neben diesem Thread auch immer auf GitHub:
https://github.com/flobernd/json-utils
Abschließend noch ein paar Minimalbeispiele:
Delphi-Quellcode:
var
S:
String;
J: TJSONObject;
R: IJSONObjectReader;
begin
S := '
{'
+ '
"number": 42,'
+ '
"object": {'
+ '
"bool": true'
+ '
},'
+ '
"array": ['
+ '
{ "str": "Hello" },'
+ '
{ "str": "World" }'
+ '
],'
+ '
"set": [1, 3]'
+ '
}';
J := TJSONObject.ParseJSONValue(S)
as TJSONObject;
try
R := TJSONObjectReader.Create(J);
// 42
ShowMessage(R.ReadInteger('
number').ToString);
// true
ShowMessage(R.ReadObject('
object').ReadBoolean('
bool').ToString);
// 'Hello World'
ShowMessage(
R.ReadArray('
array').ReadObject(0).ReadString('
str') + '
' +
R.ReadArray('
array').ReadObject(1).ReadString('
str'));
// [akTop, akBottom]
R.Reader.ReadSet<TAnchors>('
set');
// Exception: "JSON.array[0].oops" is missing or does not contain a valid STRING value."
ShowMessage(R.ReadArray('
array').ReadObject(0).ReadString('
oops'));
// 'Default Value'
ShowMessage(R.ReadArray('
array').ReadObject(0).ReadString('
oops', '
Default Value'));
finally
J.Free;
end;
end;
Delphi-Quellcode:
var
J: TJSONObject;
W: IJSONObjectWriter;
begin
J := TJSONObject.Create;
try
W := TJSONObjectWriter.Create(J);
W.WriteInteger('number', 42);
W.WriteObject('object').WriteBoolean('bool', true);
with W.WriteArray('array') do
begin
AddObject.WriteString('str', 'Hello');
AddObject.WriteString('str', 'World');
end;
// Ordinal values: [1, 3]
W.Writer.WriteSet<TAnchors>('set', [akTop, akBottom]);
// String values : ["akTop", "akBottom"]
W.Writer.WriteSet<TAnchors>('set', [akTop, akBottom], true);
// Custom strings: ["top", "bottom"]
W.Writer.WriteSet<TAnchors>('set', [akTop, akBottom], ['left', 'top', 'right', 'bottom']);
// Writes a new array or "null", if the array does not contain elements
W.WriteArrayOrNull('conditional', function(W: TJSONArrayWriter): Boolean
begin
// Add elements to the new array
// ...
Result := W.InnerObject.Count > 0;
end);
finally
J.Free;
end;
end;
Fragen und Anregungen sind wie immer willkommen.
Viele Grüße
Zacherl