AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

JSON in TFDQuery

Ein Thema von Ykcim · begonnen am 6. Jun 2023 · letzter Beitrag vom 12. Jun 2023
Antwort Antwort
Ykcim

Registriert seit: 29. Dez 2006
Ort: NRW
804 Beiträge
 
Delphi 10.4 Sydney
 
#1

JSON in TFDQuery

  Alt 6. Jun 2023, 16:12
Datenbank: MySQL • Version: 5 • Zugriff über: FireDac
Hallo Zusammen,

es geht zwar nicht wirklich um Datenbanken, aber Query und JSON, daher habe ich das Thema jetzt mal hier platziert.

Ich greife über eine API den Status von Maschinen ab. Das habe ich bislang über FDQueries auf der Datenbak gemacht, soll aber jetzt über die API laufen...

Die Abfrage soll später auf einer Serverinstance laufen, die Daten in einer Query ablegen, wo die Clients sich diese über eine Service-Abfrage rausholen...

In meinem Test-Programm habe ich eine TIdHTTP Komponente, mit der ich die Daten abfrage. Ich bekomme ein JSON in einen TStream zurück. Diesen Stream möchte ich gerne in eine FDQuery mit LoadFrom Stream laden.

Hier der Code:
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var LStream: TMemoryStream;
begin
   LStream := TMemoryStream.Create;
   Try
      IdHTTP.Get(edt_URL.Text, LStream);
      FDQuery.LoadFromStream(LStream, sfJSON);
   Finally
      LStream.Free;
   End;
end;
Leider funktioniert dieser Weg neicht, denn ich bekomme immer eine Fehlermeldung:
Ungültiges JSON-Speicherformat Position 1020 aufgetreten. Ich habe dann den Stream in eine Datei gespeichert um zu sehen, was da drinsteht:
Code:
{"error":null,"device":{"id":"4106","name":"XL106-8PL","classId":"ID_SheetfedPress","deviceStatus":{"status":"Running","activity":"Gutproduktion","speed":15000,"totalizer":96280594,"workstep":{"id":"CV__1182420004","name":"ST118242  4/4","job":{"id":"305537","name":"Panorama","jobCustomer":{"id":"022075","name":"Life e. V."},"customerOrderId":"305537"},"status":"RUNNING","amountPlanned":75075,"wastePlanned":775,"amountProduced":30293,"wasteProduced":999,"deviceId":"4106","types":["ConventionalPrinting"],"sequenceType":"SheetfedPrinting","start":"2023-06-05T11:25:55+02:00","end":"2023-06-05T13:18:09+02:00","startPlanned":null,"endPlanned":null,"setuptimePlanned":480,"prodtimePlanned":20160,"actualTimes":[{"timeTypeGroupName":"Fertigungszeit","timeTypeName":"Ausführungszeit","duration":10413},{"timeTypeGroupName":"Fertigungszeit","timeTypeName":"Rüstzeit","duration":1304}]},"employees":[{"id":"231","name":"Rosse","firstName":"Kevin"},{"id":"233","name":"Resse","firstName":"David"}]}}}
Über einen Online-JSON-Reader habe ich das geprüft, das scheint in Ordnung zu sein.
Die Position 1020 ist die vorletzte Klammer...

Hat jemand eine Idee, was ich falsch mache? Wäre eine schöne und einfache Lösung, an der jetzt schon einige Zeit scheitere...

Vielen Dank Patrick
Patrick

Geändert von Ykcim ( 6. Jun 2023 um 22:12 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.014 Beiträge
 
Delphi 12 Athens
 
#2

AW: JSON in TFDQuery

  Alt 6. Jun 2023, 17:17
Damit eine TFDQuery über JSON geladen werden kann, genügt es nicht, sie einfach mit einem in der Syntax validen JSON zu füttern. Es muss schon auch inhaltlich dem intern verwendeten JSON-Format entsprechen. Wie das aussieht bekommest du am einfachsten raus, wenn du das ResultSet einer Query als JSON-Stream abspeicherst.

Ich gehe aber mal davon aus, dass die API kein FireDAC-konformes JSON liefern soll, sondern die gezeigte Struktur. Dann kommt dein simpler Ansatz mit LoadFromStream allerdings nicht in Frage.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Benutzerbild von Union
Union

Registriert seit: 18. Mär 2004
Ort: Luxembourg
3.487 Beiträge
 
Delphi 7 Enterprise
 
#3

AW: JSON in TFDQuery

  Alt 6. Jun 2023, 21:59
Am simpelsten wäre es, wenn du die Speedmaster-Daten in einem Blob-Feld speicherst. Dann könnte dein Webservice einfach die empfangenen JSON-Strings aus der Datenbank an die Clients weiterleiten. Falls sich das Datenformat nicht ändert, könntest Du natürlich das JSON in die Felder deserialisieren und in entsprechende Tabellen schreiben. Dann hättest Du einfachere Auswertungsmöglichkeiten, mit dem Nachteil, dass du die Tabellendaten wieder serialisieren müsstest.


Zur Abfrage der Druckjobs solltest Du IMHO besser TRestClient / TRestRequest verwenden.
Ibi fas ubi proxima merces
sudo /Developer/Library/uninstall-devtools --mode=all
  Mit Zitat antworten Zitat
mytbo

Registriert seit: 8. Jan 2007
461 Beiträge
 
#4

AW: JSON in TFDQuery

  Alt 6. Jun 2023, 22:05
Über einen Online-JSON-Reader habe ich das geprüft, das scheint in Ordnung zu sein.
Der JSON-Reader aus diesem Artikel zeigt es dir auch an. Du kannst die JSON-Daten einfach über das Clipboard laden.

Bis bald...
Thomas
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
43.149 Beiträge
 
Delphi 12 Athens
 
#5

AW: JSON in TFDQuery

  Alt 7. Jun 2023, 00:15
Wenn du den Code in der DB bereits hast, dann wäre es natürlich am Einfachstens das JSON als String (VARCHAR bzw. TEXT) übergeben.

Ja, es gibt auch einen JSON-Type. Kommt aber darauf an, wie FireDAC damit umgeht. Vermutlich wird einfach alles "Unbekannte" im FireDAC wie ein VARCHAR/TEXT behandelt.
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat
Ykcim

Registriert seit: 29. Dez 2006
Ort: NRW
804 Beiträge
 
Delphi 10.4 Sydney
 
#6

AW: JSON in TFDQuery

  Alt 12. Jun 2023, 07:39
Hallo Zusammen,

Rückmeldung, wie ich das Thema vorerst angegangen bin:

Nachdem es keine "einfache" Lösung gab, habe ich das JSON manuell ausgelesen:

Delphi-Quellcode:
procedure TForm1.Get_NextJSValue(Feld: string; ActJSValue: TJSONValue; var JSObj: TJSONObject; var JSValue: TJSONValue);
begin
   JSObj := TJSONObject(ActJSValue);
   if Feld <> 'then
      JSValue := JSObj.Values[Feld];
end;

function TForm1.MyStreamToString(aStream: TStream): widestring;
var    SS: TStringStream;
begin
   if aStream <> nil then begin
      Result := '{"data":';
      SS := TStringStream.Create('');
      try
         SS.CopyFrom(aStream, 0);
         Result := Result + SS.DataString;
         Result := Result + '}';
      finally
         SS.Free;
      end;
   end else begin
      Result := '';
   end;
end;
Delphi-Quellcode:
var FirstJSObject: TJSONObject;
      SecondJSObject: TJSONObject;
      ThirdJSObject: TJSONObject;
      FourthJSObject: TJSONObject;
      FivthJSObject: TJSONObject;
      SixthJSObject: TJSONObject;
      SeventhJSObject: TJSONObject;
      EigthJSObject: TJSONObject;
      FirstArr: TJSONArray;
      FirstJSValue: TJSONValue;
      SecondJSValue: TJSONValue;
      ThirdJSValue: TJSONValue;
      FourthJSValue: TJSONValue;
      FivthJSValue: TJSONValue;
      SixthJSValue: TJSONValue;
      SeventhJSValue: TJSONValue;
      EigthJSValue: TJSONValue;
      StreamString: widestring;
      j: integer;
begin

   StreamString := Get_MData(edt_URL.Text, true);
   Get_FirstJSValue('data', StreamString, FirstJSObject, FirstJSValue);
   Try
      if Assigned(FirstJSValue) and (FirstJSValue is TJSONObject) then begin
         Get_NextJSValue('device', FirstJSValue, SecondJSObject, SecondJSValue);

         if Assigned(SecondJSValue) and (SecondJSValue is TJSONObject) then begin
            Get_NextJSValue('deviceStatus', SecondJSValue, ThirdJSObject, ThirdJSValue);

            if Assigned(ThirdJSValue) and (ThirdJSValue is TJSONObject) then begin
               Get_NextJSValue('workstep', ThirdJSValue, FourthJSObject, FourthJSValue);

               if Assigned(FourthJSValue) and (FourthJSValue is TJSONObject) then begin
                  Get_NextJSValue('job', FourthJSValue, FivthJSObject, FivthJSValue);

                  if Assigned(FivthJSValue) and (FivthJSValue is TJSONObject) then begin
                     Get_NextJSValue('jobCustomer', FivthJSValue, SixthJSObject, SixthJSValue);

                     if Assigned(SixthJSValue) and (SixthJSValue is TJSONObject) then begin
                        Get_NextJSValue('', FivthJSValue, SeventhJSObject, SeventhJSValue);

                     end;
                  end;
               end;

               Get_NextJSValue('employees', ThirdJSValue, FourthJSObject, FourthJSValue);
               if Assigned(FourthJSValue) and (FourthJSValue is TJSONArray) then begin //JSONARRAY
                  FirstArr := TJSONArray(FourthJSValue);
                  for j := 0 to FirstArr.Count - 1 do begin
                     if FirstArr.Items[j] is TJSONObject then begin
                        EigthJSObject := TJSONObject(FirstArr.Items[j]);

                     end;
                  end;
               end;
            end;
         end;
      end;
      //Daten von Objekt abfragen
      ExtracJSValues(FirstJSObject, SecondJSObject, ThirdJSObject, FourthJSObject, FivthJSObject, SixthJSObject, SeventhJSObject, EigthJSObject, nil);
   finally
      FirstJSObject.Free;
      {SecondJSObject.Free;      //Hier gibt es eine Zugriffs-Exception
      ThirdJSObject.Free;
      FourthJSObject.Free;
      FivthJSObject.Free;
      SixthJSObject.Free;
      SeventhJSObject.Free;
      EigthJSObject.Free;}

   end;
end;
Delphi-Quellcode:
rocedure TForm1.ExtracJSValues(JSObj_1, JSObj_2, JSObj_3, JSObj_4, JSObj_5,
  JSObj_6, JSObj_7, JSObj_8: TJSONObject; Qry: TFDQuery);
var JOB_Bez, JOB_Nr, JOB_Name: string;
     PLANNED_GOOD_MANOUNT: integer;
     GOOD_CYCLES: integer;
     Speed: integer;
     PERCENT_COMPLETED: string;
     Start: string;
     Dauer: string;
     OperatorName: string;
     Operator_VName: string;
     Operator_NName: string;
     OPERATION_NAME: string;
     WORK_STEP_NAME: string;
     OPERATION_KEY: string;
begin
   if not JSObj_4.TryGetValue<string>('activity', OPERATION_NAME) then
      OPERATION_NAME := '';
   if not JSObj_4.TryGetValue<integer>('speed', Speed) then
      Speed := 0;

   if not JSObj_5.TryGetValue<string>('name', WORK_STEP_NAME) then
      WORK_STEP_NAME := '';
   if not JSObj_5.TryGetValue<string>('status', OPERATION_KEY) then
      OPERATION_KEY := '';

   if not JSObj_5.TryGetValue<string>('start', Start) then
      Start := '';

   if not JSObj_6.TryGetValue<string>('name', JOB_Bez) then
      JOB_Bez := '';
   if not JSObj_6.TryGetValue<string>('id', JOB_Nr) then
      JOB_Nr := '';
   JOB_Name := JOB_Nr + ' ' + JOB_Bez;

   if not JSObj_8.TryGetValue<string>('firstName', Operator_VName) then
      Operator_VName := '';
   if not JSObj_8.TryGetValue<string>('name', Operator_NName) then
      Operator_NName := '';
   OperatorName := Operator_VName + ' ' + Operator_NName;

   if assigned(Qry) then begin
      Qry.Insert;

   end;
end;
So weit bekomme ich die gewünschten Daten aus dem JSON raus. Jetzt muss ich die noch in geeigneter Form bereitstellen...

Herzlichen Gruß und allen eine gute Woche
Patrick
Patrick
  Mit Zitat antworten Zitat
Blup

Registriert seit: 7. Aug 2008
Ort: Brandenburg
1.429 Beiträge
 
Delphi 10.4 Sydney
 
#7

AW: JSON in TFDQuery

  Alt 12. Jun 2023, 10:44
Der Code müsste beim compilieren eigentlich Meldungen über nicht initialisierte Variablen verursachen.
Sind diese Warnungen etwa ausgeschalten?

Es ist sinnvoll GetNextJSValue aufzuteilen und sinnvolle Namen für Variablen zu verwenden:
Delphi-Quellcode:
function GetFieldValue(AObject: TJSONObject; const AFieldname: string): TJSonValue;
begin
  if Assigned(AObject) and (AFieldname <> '') then
    Result := AObject.Values[Feld]
  else
    Result := nil;
end;

function GetSubObject(AObject: TJSONObject; const AFieldname: string): TJSONObject;
var
  lValue: TJSONValue;
begin
  lValue := GetFieldValue(AObject, AFieldname);
  if Assigned(lValue) and lValue is TJSONObject) then
    Result := TJSONObject(lValue)
  else
    Result := nil;
end;

function GetSubArray(AObject: TJSONObject; const AFieldname: string): TJSONArray;
var
  lValue: TJSONValue;
begin
  lValue := GetFieldValue(AObject, AFieldname);
  if Assigned(lValue) and lValue is TJSONArray) then
    Result := TJSONArray(lValue)
  else
    Result := nil;
end;
Delphi-Quellcode:
   Get_FirstJSValue('data', StreamString, obj_data, FirstJSValue);
   Try
     obj_device := GetSubObject(obj_data, 'device');
     obj_deviceStatus := GetSubObject(obj_device, 'deviceStatus');
     obj_workstep := GetSubObject(obj_deviceStatus, 'workstep');
     obj_job := GetSubObject(obj_workstep, 'job');
     obj_jobCustomer := GetSubObject(obj_job, 'jobCustomer');

     arr_employees := GetSubArray(obj_deviceStatus, 'employees');
     if Assigned(arr_employees) then
     begin
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
43.149 Beiträge
 
Delphi 12 Athens
 
#8

AW: JSON in TFDQuery

  Alt 12. Jun 2023, 12:31
Zitat:
if Assigned(lValue) and lValue is TJSONArray) then
Da fehlt 'ne Klammer, aber egal ... braucht man eh nicht
if lValue is TJSONArray then

IS prüft auf NIL.
(aber Achtung, AS läßt NIL durch)
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat
Antwort Antwort


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 07:32 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