Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Software-Projekte der Mitglieder (https://www.delphipraxis.net/26-software-projekte-der-mitglieder/)
-   -   Helper für JSON Objects Framework (System.JSON) (https://www.delphipraxis.net/191470-helper-fuer-json-objects-framework-system-json.html)

Zacherl 19. Jan 2017 18:40


Helper für JSON Objects Framework (System.JSON)
 
Liste der Anhänge anzeigen (Anzahl: 1)
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:
  1. Man muss sehr viel casten und bei praktisch jeder Operation Typ-Checks mit dem
    Delphi-Quellcode:
    is
    Operator durchführen
  2. Geht dann irgendwas schief, wird eine nichtssagende Exception ".. kann nicht in .. konvertiert werden" geschmissen, sofern man nicht selbstständig nachbessert
  3. Beim Generieren von JSON, ist die resultierende textuale Darstellung immer in unformatierter Kompaktform (sprich: unlesbar)
Hierfür habe ich die Helferklassen
Delphi-Quellcode:
IJSONArrayReader
bzw.
Delphi-Quellcode:
IJSONObjectReader
implementiert. Die Klassen sind Wrapper für
Delphi-Quellcode:
TJSONArray
bzw.
Delphi-Quellcode:
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
Delphi-Quellcode:
IJSONArrayWriter
und
Delphi-Quellcode:
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
Delphi-Quellcode:
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

BrightAngel 20. Jan 2017 15:52

AW: Helper für JSON Objects Framework (System.JSON)
 
Hab das hier grade entdeckt und lustigerweise grade vorgestern wieder was mit Json-Schema zu tun. Kennst du das? Klar, das braucht man jetzt nicht IMMER, aber vielleicht siehst du einen Anwendungsfall?

Falls du davon noch nichts gehört haben solltest: JsonSchema soll für Json Files eine formale Spezifikationsmöglichkeit definieren, die dann geparsed und ausgelesen werden kann. Sie ist selbst über ein JsonSchema spezifiziert (Ja, das ist genau die selbe Idee wie auch bei XML). So wie das aussieht, hat man da ernsthafte Absichten mit IETF usw. Vielleicht ist das ein Blick wert und vielleicht magst du das ja auch noch integrieren.

Vielleicht kann man dann auch automatisiert DelphiCode aus dem JsonSchema erzeugen, so wie auch bei XML? (Die Idee hatte bestimmt schonmal jemand auch für Delphi, aber vielleicht bringt das hier auch was?)

Brighty

Zacherl 21. Jan 2017 08:54

AW: Helper für JSON Objects Framework (System.JSON)
 
Zitat:

Zitat von BrightAngel (Beitrag 1359415)
Vielleicht kann man dann auch automatisiert DelphiCode aus dem JsonSchema erzeugen, so wie auch bei XML? (Die Idee hatte bestimmt schonmal jemand auch für Delphi, aber vielleicht bringt das hier auch was?)

Sieht interessant aus. Besonders eine automatische Verifizierung von Constraints könnte für mich auch noch nützlich sein.

Code Generierung aus einem festen Schema und automatisches (De-)serialisieren liegt momentan allerdings nicht in meinem Fokus, da ich dies für mein aktuelles Projekt nicht anwenden kann.


Alle Zeitangaben in WEZ +1. Es ist jetzt 06:46 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz