XML und JSON sind inzwischen zu den wichtigsten Standards für die Datenspeicherung und den Informationsaustausch zwischen Computersystemen geworden. Als Reaktion auf Benutzeranfragen wurden Klassen für die Arbeit mit XML und JSON zu FastScript hinzugefügt. In diesem Artikel sehen wir uns genauer an, wie man mit diesen Klassen arbeitet, über welche Eigenschaften und Methoden sie verfügen und wie man Berichte aus Skriptcode erstellt.
In Skripten werden 2 Klassen verwendet, um mit XML zu arbeiten. Diese Klassen sind auf maximale Leistung und geringen Speicherverbrauch ausgerichtet.
TfrXMLDocument – ist eine Klasse, die die Funktionalität eines XML-Dokuments kapselt. Die folgenden Eigenschaften und Methoden sind in dieser Klasse verfügbar.
Eigenschaften und Methoden der Klasse | Beschreibung |
procedure SaveToStream(Stream: TStream); |
speichert XML im übergebenen Stream |
procedure LoadFromStream(Stream: TStream; AllowPartialLoading: Boolean = False); |
lädt XML aus dem übergebenen Stream hoch |
procedure SaveToFile(const FileName: string); |
speichert XML in einer Datei mit dem angegebenen Namen |
procedure LoadFromFile(const FileName: string); |
lädt XML aus einer Datei mit dem angegebenen Namen hoch |
procedure Clear; |
Entfernt alle XML-Knoten aus dem Baum außer dem Stammknoten. Der Inhalt des Knotens wird NICHT geleert. |
property Root: TfrXMLItem; |
ermöglicht den Zugriff auf das Stammelement des Baums |
property AsText: string; |
ermöglicht, XML als Zeichenkette abzurufen und festzulegen (mit dieser Eigenschaft können Sie beispielsweise XML mithilfe einer Berichtsvariablen an ein Berichtsskript übergeben) |
property AutoIndent: Boolean; |
legt fest, wie die XML-Ausgabe generiert werden soll: als einzelne Zeile oder als eingerückter Text |
TfrXMLNode – a class that encapsulates the properties of an XML document node.
You cannot create an object of this type directly. It is created by calling the Add() method of the element to which you want to add a child element. Let’s take a closer look at the properties and methods of the TfrXMLNode class.
Eigenschaften und Methoden der Klasse | Beschreibung |
function Add(AName:string):TfrXMLItem; |
erstellt ein untergeordnetes TfrXMLItem Element mit dem angegebenen Namen und gibt es zurück |
procedure Clear; |
löscht die Liste der untergeordneten Elemente |
procedure InsertItem(Index:integer; AItem: TfrXMLItem); |
fügt ein untergeordnetes Element an der angegebenen Position ein; das Element kann zu einem anderen Dokument gehören |
function Find(AName:string):Integer; |
sucht unter den untergeordneten Elementen ein TfrXMLItem mit dem angegebenen Namen und gibt es zurück. Wenn das Element nicht gefunden wird, wird -1 zurückgegeben. Wenn es mehrere Elemente mit dem angegebenen Namen gibt, wird das erste von ihnen zurückgegeben |
function FindItem(AName:string):TfrXMLItem; |
sucht unter den untergeordneten Elementen ein TfrXMLItem mit dem angegebenen Namen und gibt es zurück. Wenn das Element nicht gefunden wird, wird ein neues Element mit dem angegebenen Namen erstellt und zurückgegeben. Wenn es mehrere Elemente mit dem angegebenen Namen gibt, wird das erste von ihnen zurückgegeben. |
function Root: TfrXMLItem; |
gibt das Stammelement des Dokuments zurück |
property Count:Integer; |
Gibt die Anzahl der untergeordneten Knoten des Elements zurück |
property Items[AIndex:Integer]: TfrXMLItem; |
gibt ein untergeordnetes Element anhand seines Indexes zurück |
property Prop[AName:string]:string; |
gibt den Wert des Knotenattributs mit dem angegebenen Namen zurück oder legt ihn fest |
property Name: string; |
Name des Element-Tags |
property Parent: TfrXMLItem; |
Name des übergeordneten Elements. Bei Root ist es gleich nil |
property Text:string; |
Eine Zeichenkette mit einer Liste von Knotenparametern in der folgenden Form: Name1=”Value1” Name2=”Value2”… Sonderzeichen sind in dieser Zeichenkette Escapezeichen. |
procedure Delete(AIndex: Integer); |
löscht das untergeordnete Element mit dem AIndex-Index |
procedure DeleteProp(const APropName: string); |
löscht ein Knotenattribut mit dem angegebenen Namen |
property Value: string; |
Text, der zwischen dem öffnenden und dem schließenden Tag des Elements steht |
function PropExists(APropName:string):Boolean; |
hilft festzustellen, ob ein Element ein Attribut mit dem angegebenen Namen hat |
function IndexOf(AItem: TfrXMLItem):Integer; |
gibt den Index des übergebenen Elements zurück |
function GetPropNames(ANames:TStrings); |
Füllt die übergebene Zeichenkettenliste mit TfrXMLItem-Attributnamen |
Versuchen wir, einen Bericht mithilfe von Klassen für das Arbeiten mit XML zu erstellen. Als Beispiel werden wir Daten aus der Datei „country.xml“ in einem Bericht anzeigen, dabei jedoch nicht TClientDataSet verwenden, sondern auf die XML-Datei direkt zugreifen. Erstellen Sie einen neuen Bericht, wechseln Sie in den Bearbeitungsmodus und fügen Sie MasterData in den Bericht ein. Und obendrauf fügen Sie mehrere Memos hinzu, um die Daten anzuzeigen. Erstellen Sie außerdem das OnBeforePrint Ereignis für das MasterData-Objekt.
Der endgültige Code des Berichtsskripts sieht wie folgt aus:
var doc: TfrXMLDocument; item: TfrXMLItem; IIndex: Integer; procedure MasterData1OnBeforePrint(Sender: TfrxComponent); var cur: TfrXMLItem; S: string; begin cur := item[IIndex]; mmNum.Text := cur.Prop['Code']; mmName.Text := cur.Prop['Name']; mmCapital.Text := cur.Prop['Capital']; mmArea.Text := cur.Prop['Area']; mmPopulation.Text := cur.Prop['Population']; mmContinent.Text := cur.Prop['Continent']; Inc(IIndex); end; begin Doc := TfrXMLDocument.Create; Doc.LoadFromFile('..\..\Data\country.xml'); item := Doc.Root[1]; IIndex := 0; MasterData1.RowCount := item.count; end.
Führen Sie den Bericht aus. Zur besseren Lesbarkeit haben wir auch Spaltenüberschriften hinzugefügt.
Wechseln wir vom XML-Format zu JSON-Format. FastReport verwendet auch 2 Klassen um mit JSON zu arbeiten. Das sind TfrJSON und TfrJSONArray. In diesem Fall sind diese Klassen nur ein Wrapper um Klassen von System.JSON in der Delphi Plattform oder ähnliche Klassen in Lazarus.
TfrJSONObject ist eine Klasse, die Funktionen für die Arbeit mit JSON-Objekten kapselt.
Eigenschaften und Methoden der Klasse | Beschreibung |
function Create(const JSONstring: string); |
erzeugt ein JSON-Objekt und füllt es mit der übergebenen Zeichenkette |
function IsValid:Boolean; |
gibt True zurück, wenn das Objekt gültiges JSON enthält |
procedure LodFromFile(const AFilName:string); |
lädt Daten aus der angegebenen Datei. Wenn das Objekt Daten enthält, gehen diese verloren |
procedure LoadFromtStream(const AStream:TStream); |
lädt Daten aus dem übergebenen Stream. Wenn das Objekt Daten enthält, gehen diese verloren |
function ToString:string; |
gibt eine Zeichenkette zurück, die die alphabetische Darstellung des JSON-Objekts enthält |
procedure SaveToFile(const AFileName: string); |
speichert die alphabetische Darstellung eines JSON-Objekts in einer Datei |
procedure SaveToStream(AStream: TStream |
speichert die alphabetische Darstellung eines JSON-Objekts in einem Stream |
function IsNameExists(const AName:string); |
gibt True zurück, wenn das Objekt eine Eigenschaft mit dem angegebenen Namen hat |
function IsNameValueExists(const Name, Value: string): boolean; |
gibt True zurück, wenn das Objekt eine Eigenschaft mit dem angegebenen Namen hat und ihr Wert mit dem angegebenen Wert übereinstimmt |
function Count:Integer; |
gibt die Gesamtzahl der Eigenschaften eines JSON-Objekts zurück |
property Names[AIndex:Integer]:string; |
gibt den Namen der Eigenschaft mit dem angegebenen Index zurück |
Property ValueType[IndexOrName:Variant]:TfrJSONType; |
gibt den Typ der Eigenschaft mit dem angegebenen Index oder Namen zurück. Der Typ kann einer der folgenden Werte sein: – jsUnknown – der Eigenschaftstyp ist unbekannt (es ist ein Parsing-Fehler aufgetreten oder es gibt keine Eigenschaft mit diesem Index oder Namen). – jsNumber – die Eigenschaft ist vom numerischen Typ. – jsString – die Eigenschaft ist vom Zeichenkettentyp. – jsBoolean – die Eigenschaft ist vom Booleschen Typ (true/false). – jsNull – der Eigenschaftstyp ist null (der Wert ist nicht festgelegt). – jsList – der Eigenschaftentyp ist ein JSON-Array. – jsObject – der Eigenschaftstyp ist ein verschachteltes JSON-Objekt. |
Mit den folgenden Eigenschaften können Sie die Werte eines JSON-Objekts in der einen oder anderen Form abrufen. Bei allen diesen Eigenschaften erfolgt die Indizierung entweder über die Eigenschaftsnummer in der Eigenschaftsliste oder über ihren Namen. Beachten Sie, dass AsString[‘1’] <> AsString[1] nicht gleich sind. Auch wenn Sie versuchen, den Eigenschaftswert mit einer ungeeigneten Methode abzurufen, können Fehler auftreten und falsche Eigenschaftswerte zurückgegeben werden. Wenn eine Eigenschaft beispielsweise vom Typ „Objekt“ oder „Array“ ist und Sie versuchen, ihren Wert mithilfe der Eigenschaft „AsString“ abzurufen, erhalten Sie eine leere Zeichenkette zurück. In anderen Fällen gibt AsString eine Zeichenkettendarstellung des Eigenschaftswerts zurück.
Eigenschaften und Methoden der Klasse | Beschreibung |
property AsObject[IndexOrName: Variant]: TfrxJSON; |
ermöglicht es, ein verschachteltes JSON-Objekt abzurufen |
property AsArray[IndexOrName: Variant]: TfrJSONArray; |
ermöglicht es, ein verschachteltes JSON-Array abzurufen |
Property AsString[IndexOrName: Variant]: string; |
ermöglicht es, den Eigenschaftswert als Zeichenkette anzurufen. Diese Methode kann auf Eigenschaften von anderen Typen als Objekten und Arrays angewendet werden. Für logische Werte wird eine Zeichenkette mit dem Wert „True“ oder „False“ zurückgegeben. Bei Zahlen mit einem Bruchteil wird unabhängig von den regionalen Einstellungen immer ein Punkt als Dezimaltrennzeichen verwendet. |
property AsNumber[IndexOrName: Variant]: Extended; |
ermöglicht es, den Eigenschaftswert als Zahl abzurufen. Wenn die Eigenschaft nicht numerisch ist, wird 0 zurückgegeben |
property AsBoolean[IndexOrName: Variant]: Boolean; |
ermöglicht es, den Eigenschaftswert als Booleschen Wert abzurufen. Wenn die Eigenschaft von einem anderen Typ ist, wird False zurückgegeben. |
Um ein Nullfeld zurückzugeben, müssen Sie sich etwas mehr Mühe geben. Im Allgemeinen gibt es keine solche Methode. Sie können jedoch die ValueType-Eigenschaft verwenden, die für ein Nullfeld jsNull zurückgibt, oder die AsString-Eigenschaft verwenden, die die Zeichenkette „null“ zurückgibt.
Betrachten wir näher die Methoden zur Behandlung von Objekteigenschaften.
Methoden der Klasse | Beschreibung |
Procedure Delete(AName: String); |
löscht die Eigenschaft des Objekts mit dem angegebenen Namen |
function AddObject(const AName: string): Integer |
fügt dem Objekt ein leeres untergeordnetes Objekt mit dem angegebenen Namen hinzu |
function AddArray(const AName: string): Integer; |
fügt dem Objekt ein leeres Array mit dem angegebenen Namen hinzu |
function AddString(const AName, AValue: string): Integer; |
fügt dem Objekt eine Zeichenkette mit dem angegebenen Namen hinzu |
function AddBool(const AName: string; AValue: boolean): Integer; |
fügt dem Objekt einen booleschen Wert mit dem angegebenen Namen hinzu |
function AddNumber(const AName: string; AValue:Extended): Integer; |
fügt dem Objekt einen numerischen reelen Wert mit dem angegebenen Namen hinzu |
function AddInteger(const AName: string; AValue:Integer): Integer; |
fügt dem Objekt einen numerischen Ganzzahlwert mit dem angegebenen Namen hinzu |
function AddNull(const AName: string): Integer; |
Fügt dem Objekt einen Nullwert mit dem angegebenen Namen hinzu |
Die Methoden geben eine Ganzzahl zurück, die den Index des hinzugefügten Elements in der Liste aller Felder darstellt.
Alle Methoden, die einem Objekt Felder hinzufügen, überwachen nicht, ob bereits Felder mit gleichem Namen vorhanden sind. Die Verantwortung dafür liegt beim Programmierer, der diese Klasse verwendet. Diese Überwachung ist notwendig, um zu verhindern, dass mehrere Felder mit denselben Namen in der JSON-Ausgabe erscheinen. Das Verhalten des Systems in diesem Fall ist nicht definiert - es hängt vom Parser ab, der das empfangene JSON weiter parst. Das bedeutet, dass jeder beliebige Wert mit demselben Namen in der Arbeit erscheinen kann!
Die Klasse TJSONArray kapselt Methoden und Eigenschaften für die Arbeit mit JSON-Arrays. Die Grundeigenschaften sind grundsätzlich gleich, aber auf sie kann nur per Index zugegriffen werden. Es werden nur Operationen unterstützt, die auf ein beliebiges Element zugreifen, seinen Wert abfragen oder ein Element löschen. Es ist nicht möglich, Elemente im Array zu verschieben oder ihren Typ zu ändern. Betrachten wir die Eigenschaften und Methoden zum Abrufen der Werte von Elementen in dieser Klasse.
Eigenschaften und Methoden der Klasse | Beschreibung |
function Count:Integer; |
gibt die Anzahl der Elemente in einem Array zurück |
property ValueType[AIndex: Integer]: TfrJSONType; |
gibt den Typ des Array-Elements mit dem angegebenen Index zurück |
property AsObject[AIndex: Integer]: TfrJSONObject; |
gibt ein Objekt zurück, das den Zugriff auf ein Array-Element als JSON-Objekt ermöglicht |
Property AsArray[AIndex: Integer]: TfrJSONArray |
gibt ein Objekt zurück, das den Zugriff auf ein Array-Element als JSON-Array ermöglicht |
property AsString[AIndex: Integer]: string; |
gibt den Wert eines Array-Elements als Zeichenkette zurück |
property AsNumber[AIndex: Integer]: Extended; |
gibt den Wert eines Array-Elements als Zahl zurück |
property AsBoolean[AIndex: Integer]: Boolean; |
gibt den Wert eines Array-Elements als booleschen Wert zurück |
Wie bei einem Objekt sollten Sie beim Abrufen von Werten von Array-Elementen vorsichtig sein. Je nach dem Wert-Typ sollten Sie die entsprechende Methode verwenden, um ihn abzurufen.
Nachfolgend werden Methoden zum Bearbeiten von Elementen eines JSON-Arrays beschrieben.
Eigenschaften und Methoden der Klasse | Beschreibung |
procedure Delete(AIndex:Integer); |
lLöscht ein Array-Element mit einem angegebenen Index |
function AddObject: Integer; |
fügt ein verschachteltes JSON-Objekt am Ende des Arrays hinzu |
function AddArray: Integer; |
fügt ein verschachteltes JSON- Array am Ende des Arrays hinzu |
function AddString(const AValue: string): Integer; |
fügt eine Zeichenkette am Ende des Arrays hinzu |
function AddBool(AValue: boolean): Integer; |
fügt einen booleschen Wert am Ende des Arrays hinzu |
function AddNumber(AValue:Extended): Integer; |
fügt eine reelle Zahl am Ende des Arrays hinzu |
function AddInteger(AValue: Integer): Integer; |
fügt eine Ganzzahl am Ende des Arrays hinzu |
function AddNull: Integer; |
fügt Null am Ende des Arrays hinzu |
Alle diese Methoden geben die Position des zum Array hinzugefügten Elements zurück. Sie können kein Element in die Mitte eines Arrays einfügen – dies ist eine Einschränkung der Basisklassen in System.JSON.
Bei der Verwendung von Objekten ist zu beachten, dass alle Wrapper-Objekten nach ihrer Verwendung aus dem Code entfernt werden müssen.
Bitte beachten Sie, wenn Sie ein untergeordnetes Objekt oder Array abrufen möchten und stattdessen ein Skalarwert oder überhaupt kein Wert vorhanden ist, gibt die Eigenschaft „nil“ zurück und der weitere Zugriff auf eine solche Eigenschaft führt zu einer Ausnahme. Sie sollten keine Methoden verwenden, die nicht für die Arbeit mit Werten bestimmter Typen ausgelegt sind, die in einem Element gespeichert sind, um diese Werte abzurufen.
Beachten Sie auch, dass bei den Namen der Objekteigenschaften zwischen Groß- und Kleinschreibung unterschieden wird, d.h., „Item1“ und „item1“ sind unterschiedliche Namen!
Sie können den Objektelementen nur dann Werte zuweisen, wenn der Typ dieser Werte mit dem Typ übereinstimmt, der dem Element zugewiesen ist. Wenn Sie einen Elementtyp neu definieren müssen, löschen Sie dieses Element und fügen Sie ein neues Element mit demselben Namen und dem erforderlichen Typ hinzu. Es ist nicht möglich, neue Werte für Array-Elemente einzugeben. Sie müssen ein JSON-Array löschen und es dann mit den gewünschten Werten neu erstellen.
Verwenden wir die oben beschriebenen Klassen und schreiben ein Skript, das Daten (ohne Typinformationen) aus einer XML-Datei extrahiert und in ein JSON-Objekt einfügt.
procedure XMLToJSON; var XML: TfrXMLDocument; xi, xi1: TfrXMLItem; I, J: Integer; JO: TfrJSONObject; JA, JA1: TfrJSONArray; SL: TStringList; begin XML:=TfrXMLDocument.Create; XML.LoadFromFile('.\..\..\data\country.xml'); JO:=TfrJSONObject.Create('{}'); JO.AddArray('data'); JA:=JO.AsArray['data']; xi:=X.Root[1]; xi1:=xi.Items[0]; SL:=TStringList.Create; xi1.GetParamNames(SL); for I:=0 to xi.Count - 1 do begin xi1:=xi.Items[I]; JA1:=JA.AsArray[JA.AddArray]; for J:=0 to SL.Count-1 do JA1.AddString(xi1.Prop[SL[J]]); JA1.Free; end; JA.Free; JO.SaveToFile('.\..\..\data\country.json'); JO.Free; SL.Free; end;
Versuchen wir nun, einen Bericht auf der Grundlage der erhaltenen Daten zu erstellen (fügen Sie die entsprechenden Komponenten MasterData und Memo auf der Berichtsseite ein):
var J:TfrJSONObject; A:TfrJSONArray; curr:Integer; procedure MasterData1OnBeforePrint(Sender: TfrxComponent); var A1:TfrJSONArray; begin A1:=A.AsArray(curr); Memo1.Memo.Text:=A1.AsString[0]; Memo2.Memo.Text:=A1.AsString[1]; Memo3.Memo.Text:=A1.AsString[2]; Memo4.Memo.Text:=A1.AsString[3]; Memo5.Memo.Text:=A1.AsString[4]; Memo6.Memo.Text:=A1.AsString[5]; A1.Free; curr := curr + 1; end; begin J:=TfrJSONObject.Create(''); J.LoadFromFile('.\..\..\data\country.json'); A:=J.AsArray[0]; MasterData1.RowCount:=A.Count; curr:=0; end.
Sie können diese Objekte auch im Delphi-Code verwenden - verbinden Sie einfach die frXML- und frJSON-Module in der uses-Anweisung.
In diesem Artikel haben wir die Verwendung von Klassen für die Arbeit mit JSON und XML in FastReport betrachtet. Wenn Sie sowohl in Delphi als auch in Lazarus nicht nur Berichte entwickeln, wird die Verwendung dieser Klassen Probleme reduzieren, wenn Sie von einer Umgebung zur anderen wechseln. In beiden IDEs ist das Verhalten gleich.
Zu Ihrem Komfort haben wir zwei Beispiele beigefügt, die die Erstellung von XML- und JSON-basierten Berichten veranschaulichen. Sie können sie sofort in FastReportDemo öffnen und ausführen, das im Lieferumfang von FastReport enthalten ist. Möglicherweise müssen Sie die Berichte vor dem Start konfigurieren – geben Sie den richtigen Pfad zur Datendatei „country.xml“ an. Sie befindet sich sowohl im DemoCenter als auch im Ordner mit den Beispielen.