AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Projekte Helper für JSON Objects Framework (System.JSON)

Helper für JSON Objects Framework (System.JSON)

Ein Thema von Zacherl · begonnen am 19. Jan 2017 · letzter Beitrag vom 21. Jan 2017
Antwort Antwort
Benutzerbild von Zacherl
Zacherl
Registriert seit: 3. Sep 2004
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 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 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
Angehängte Dateien
Dateityp: pas Utils.JSON.pas (48,1 KB, 33x aufgerufen)
Projekte:
- GitHub (Profil, zyantific)
- zYan Disassembler Engine ( Zydis Online, Zydis GitHub)

Geändert von Zacherl (19. Jan 2017 um 23:59 Uhr)
 
BrightAngel
 
#2
  Alt 20. Jan 2017, 16:52
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
  Mit Zitat antworten Zitat
Benutzerbild von Zacherl
Zacherl

 
Delphi 10.2 Tokyo Starter
 
#3
  Alt 21. Jan 2017, 09:54
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.
  Mit Zitat antworten Zitat
Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 19:37 Uhr.
Powered by vBulletin® Copyright ©2000 - 2020, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2019 by Daniel R. Wolf