Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   Problem bei JSON-Abfrage (https://www.delphipraxis.net/205527-problem-bei-json-abfrage.html)

Maekkelrajter 20. Sep 2020 13:13

Problem bei JSON-Abfrage
 
der Einsicht folgend, dass mein Programm zum Übernehmen von Metadaten von Spotify via HTML - Sources vom Design her in eine Sackgasse geraten ist,(siehe hier) habe ich mich intensiv mit der Entwicklung eines REST-Clients für die Abfrage der Daten über die Spotify Web-Api beschäftigt. Nach mühevoller Einarbeitung mit den spärlich zur Verfügung stehenden Informationen der Delphi - Hilfe und einem einzigen und dazu noch ziemlich unzulänglichen Beispielprogramm bin ich nun tatsächlich soweit, dass alles mehr oder weniger wunschgemäß funktioniert: Anmeldung bzw. Authentifizierung bei Spotify, Laden der Datensätze als JSON-Struktur und schließlich deren Auswertung. Zum Auslesen der Daten benutze ich die von System.JSON zur Verfügung gestellten Methoden.
Beispiel:
Delphi-Quellcode:
Function TSpManager.GetPlaylistTracks(PlaylistID: string; List:TStrings):string;   // Liste der Track-IDs aus Playlist holen
  var ResponseValue, subitem,oid,track:TJSONValue;
      oItems: TJSONArray;
      PlName: string;
begin
  result:= '';
  RESTRequest1.Resource:= 'v1/' + 'playlists/' + PlaylistID + '/tracks';   // Accesspoint der Spotify Web-API
  RestRequest1.Execute;
  ResponseValue:= RestResponse1.JSONValue;
  If ResponseValue = NIL Then exit;
  PlName:= TJSONObject(ResponseValue).GetValue('name').Value; // Hier kann es krachen, wenn z.B 'Name' statt 'name' oder wenn 'name' nicht existiert
  oItems := TJSONArray(TJSONObject(ResponseValue).GetValue('items'));
  for subitem in oItems do
  begin
    track:= TJSONObject(subtem).GetValue('track');
    oID:= TJSONObject(track).GetValue('id');
    List.Add(oID.Value);
  end;
  result:= PlName;
end;
Die Beispiel-Funktion liefert in 'List' eine Liste mit den Spotify-IDs aller Tracks der Playlist, mit denen dann die eigentlichen Trackdaten geladen und ausgewertet werden können. Der Rückgabewert ist der Name der Playlist. Das funktioniert, wie gesagt, normalerweise einwandfrei. Wenn aber der gesuchte Schlüssel nicht gefunden wird, wirft System.JSON.TJSONObject.GetValue() eine Accessviolation ('Lesen von Adresse 00000000'). Normalerweise würde ich in einem solchen Fall einen Leerstring als Rückgabewert erwarten, aber keine Exception. Was mache ich da falsch?

Gruß LP

DeddyH 20. Sep 2020 15:31

AW: Problem bei JSON-Abfrage
 
Versuch mal, ob das den Fehler (zumindest an der Stelle) behebt:
Delphi-Quellcode:
if Assigned(TJSONObject(Value).GetValue('name')) then
  PlName := TJSONObject(Value).GetValue('name').Value;
Falls ja, könnte man über eine lokale Variable vom Typ TJSONValue nachdenken, die man zuerst befüllt, im Anschluss auf nil prüft und erst danach versucht, den Wert auszulesen.

[edit] Oder man benutzt die generische Funktion TryGetValue<T> von TJSONObject, das ist etwas bequemer.
Delphi-Quellcode:
if not TJSONObject(Value).TryGetValue<string>('name', PlName) then
  PlName := '';
[/edit]

Maekkelrajter 20. Sep 2020 16:53

AW: Problem bei JSON-Abfrage
 
Zitat:

Zitat von DeddyH (Beitrag 1473908)
Versuch mal, ob das den Fehler (zumindest an der Stelle) behebt:
Delphi-Quellcode:
if Assigned(TJSONObject(Value).GetValue('name')) then
  PlName := TJSONObject(Value).GetValue('name').Value;
Falls ja, könnte man über eine lokale Variable vom Typ TJSONValue nachdenken, die man zuerst befüllt, im Anschluss auf nil prüft und erst danach versucht, den Wert auszulesen.

Der Fehler wird so tatsächlich behoben, oder besser gesagt, umgangen. Die Idee mit der lokalen Variablen habe ich mit einer Funktion realisiert, gewissermaßen ein Wrapper für den eigentlichen Aufruf von GetValue. Der hilft auch den Code aller übrigen Methoden, die diese Funktion benutzen, deutlich zu vereinfachen:
Delphi-Quellcode:
Function TSpManager.GetValue(value:TJSONValue; key: string): String;
  var jv: TJSONValue;
begin
  result := '';
  jv := TJSONObject(value).GetValue(lowercase(key));
  If jv <> NIL Then result := jv.value;
end;

Zitat:

Zitat von DeddyH (Beitrag 1473908)
[edit] Oder man benutzt die generische Funktion TryGetValue<T> von TJSONObject, das ist etwas bequemer.
Delphi-Quellcode:
if not TJSONObject(Value).TryGetValue<string>('name', PlName) then
  PlName := '';


Das ist ja noch 'cooler'. So funktioniert's tatsächlich:
Delphi-Quellcode:
Function TSpManager.GetValue(value:TJSONValue; key: string): String;
begin
  if not TJSONObject(Value).TryGetValue<string>(lowercase(key), result) then
    result := '';
end;
Vielen Dank für die Tipps, aber das alles sind ja eigentlich 'nur' Workarounds. Ich halte es für einen veritablen Bug, wenn in System.JSON intern übergebene Parameter zu einer Accessviolation führen, weil sie nicht auf Validität überprüft werden.

Gruß LP

DeddyH 20. Sep 2020 16:56

AW: Problem bei JSON-Abfrage
 
Nunja, GetValue gibt einen TJSONValue zurück, welcher eine Klasse ist. Greift man auf den Wert einer nicht instanzierten Klasse zu, macht es eben Rumms, deshalb sind das keine Workarounds, sondern lediglich defensive Programmierung.

Uwe Raabe 20. Sep 2020 17:04

AW: Problem bei JSON-Abfrage
 
Kannst du mal als Beispiel das JSON für eine funktionierende Antwort hier posten? Das müsste noch wesentlich eleganter gehen.

Auch deine aktuelle Lösung für die Authentifizierung würde ich gerne mal sehen (natürlich ohne echte Daten). Da ist oft auch noch Raum für Vereinfachungen.

Maekkelrajter 20. Sep 2020 22:33

AW: Problem bei JSON-Abfrage
 
Liste der Anhänge anzeigen (Anzahl: 2)
Zitat:

Zitat von Uwe Raabe (Beitrag 1473915)
Kannst du mal als Beispiel das JSON für eine funktionierende Antwort hier posten? Das müsste noch wesentlich eleganter gehen.

Auch deine aktuelle Lösung für die Authentifizierung würde ich gerne mal sehen (natürlich ohne echte Daten). Da ist oft auch noch Raum für Vereinfachungen.

Ich war ja froh, als ich das Ding endlich am laufen hatte. Ich zweifele auch nicht im geringsten daran dass der Code noch verbesserungsfähig ist. Hier also die Authentifizierungs Routinen, die ich übrigens zum Teil aus dem Beispiel-Programm abgekupfert habe:
Delphi-Quellcode:

{
type
    TSpManager = class(TForm)
    [...]
    IdHTTPServer1: TIdHTTPServer;
    OAuth2Authenticator1: TOAuth2Authenticator;
    RESTClient1: TRESTClient;
    RESTRequest1: TRESTRequest;
    RESTResponse1: TRESTResponse;
    procedure IdHTTPServer1CommandGet(AContext: TIdContext;
      ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
    [...]
 }

Procedure TSpManager.SendData(s:string);
 var
    CDStruct: TCopyDataStruct;
    copytext: ansistring;
begin
  copytext:= s;
  begin
    CDStruct.dwData:= 0;
    CDStruct.cbData:= length(Copytext) + 1;
    CDStruct.lpData:= @CopyText[1];
    SendMessage(MainHandle, WM_COPYDATA,1,integer(@CDStruct));
  end;
end;


Procedure TSpManager.AuthorizeSpotifyAccess;
begin
  IdHTTPServer1.Active := True;
  OAuth2Authenticator1.ResetToDefaults;
  OAuth2Authenticator1.ClientID := Stcf.AccessData.clientID;                     // Bei der Registrierung von Spotify zugeteilt (lokal gespeichert)
  OAuth2Authenticator1.ClientSecret := Stcf.AccessData.ClientSecret;             // Bei der Registrierung von Spotify zugeteilt (lokal gespeichert)
  OAuth2Authenticator1.AuthorizationEndpoint := 'https://accounts.spotify.com/authorize';
  // Bereiche, auf die Benutzerzugriff erlaubt sein soll
  OAuth2Authenticator1.Scope := 'playlist-read-private user-read-private user-library-read';
  // Weiterleitungsseite mit dem aktuellen AUTORISIERUNGSCODE
  OAuth2Authenticator1.RedirectionEndpoint := 'http://localhost:9090';
  // Die Definition des URI zum Generieren des AUTORISIERUNGSCODES
  ShellExecute(0, 'OPEN', PChar(Self.OAuth2Authenticator1.AuthorizationRequestURI), '', '', SW_SHOWNORMAL);
end;


procedure TSpManager.IdHTTPServer1CommandGet(AContext: TIdContext;
  ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
  var s:string;
begin
  // Abfrage-String validiert
  if (ARequestInfo.Params.IndexOfName('code') = -1) then Exit;
  // Definition des ACCESS CODE, den wir von der Weiterleitungsseite abrufen
  OAuth2Authenticator1.AuthCode := ARequestInfo.Params.Values['code'];
  // End-point für die Generierung des ACCESS TOKEN
  OAuth2Authenticator1.AccessTokenEndpoint := 'https://accounts.spotify.com/api/token';
  // Definieren des ACCESS TOKEN aus dem AUTORISIERUNGSCODE
  OAuth2Authenticator1.ChangeAuthCodeToAccesToken;
  // Antwort an den Browser
  AResponseInfo.ContentText := '<html><body><script language=javascript>window.close();</script>Vielen Dank, dass Sie den Zugriff erlaubt haben!</body></html>';
  // Operation erfolgreich!
  s:= OAuth2Authenticator1.AccessToken;
  SendData(s);                                           // Schickt die Daten nur zur Information an das Hauptfenster;
  FAuthorized:= s <> '';                                 // Boolean als Flag
end;
Im Anhang ein JSON mit Playlist-Daten, das Spotify nach einem Request mit dem accesspoint '/v1/playlists/{playlist_id}' liefert. Für meine Zwecke werden nur wenige Daten ausgelesen und in Zieldateien geschrieben (wenn vorhanden)
Für Anregungen und Verbesserungsvorschläge bin ich ausgesprochen dankbar!

Gruß LP

Uwe Raabe 20. Sep 2020 23:53

AW: Problem bei JSON-Abfrage
 
Ich baue mir da immer anhand der API passende Objektstrukturen zusammen. Dann wird der Abruf nicht nur wesentlich simpler, man bekommt auch gleich die Typsicherheit dazu. Ich habe hier nur einen Teil der Felder deklariert. Alle übrigen werden ignoriert. Das lässt sich relativ leicht erweitern.
Delphi-Quellcode:
uses
  System.JSON, REST.Json;

type
  TArtist = class
  private
    FName: string;
  public
    property Name: string read FName;
  end;

  TAlbum = class
  private
    FArtists: TArray<TArtist>;
    FName: string;
  public
    property Artists: TArray<TArtist> read FArtists;
    property Name: string read FName;
  end;

  TTrack = class
  private
    FAlbum: TAlbum;
    FArtists: TArray<TArtist>;
    FId: string;
    FName: string;
  public
    property Album: TAlbum read FAlbum;
    property Artists: TArray<TArtist> read FArtists;
    property Id: string read FId;
    property Name: string read FName;
  end;

  TUser = class
  private
    FDisplay_name: string;
  public
    property Display_name: string read FDisplay_name;
  end;

  TPlaylistTrack = class
  private
    FAdded_at: TDateTime;
    FAdded_by: TUser;
    FIs_local: Boolean;
    FTrack: TTrack;
  public
    property Added_at: TDateTime read FAdded_at;
    property Added_by: TUser read FAdded_by;
    property Is_local: Boolean read FIs_local;
    property Track: TTrack read FTrack;
  end;

  TPaging<T: class> = class
  private
    FHref: string;
    FItems: TArray<T>;
    FLimit: Integer;
    FNext: string;
    FOffset: Integer;
    FPrevious: string;
    FTotal: Integer;
  public
    property Href: string read FHref;
    property Items: TArray<T> read FItems;
    property Limit: Integer read FLimit;
    property Next: string read FNext;
    property Offset: Integer read FOffset;
    property Previous: string read FPrevious;
    property Total: Integer read FTotal;
  end;

  TPlayList = class
  private
    FName: string;
    FTracks: TPaging<TPlaylistTrack>;
  public
    property Name: string read FName;
    property Tracks: TPaging<TPlaylistTrack> read FTracks;
  end;
Der eigentliche Abrufcode beschränkt auf die im Eingangspost gezeigten Daten sieht dann so aus:
Delphi-Quellcode:
function TSpManager.GetPlaylistTracks(PlaylistID: string; List: TStrings): string; // Liste der Track-IDs aus Playlist holen
var
  playlist: TPlaylist;
  track: TPlaylistTrack;
begin
  result := '';
  RESTRequest1.Resource := 'v1/playlists/{PlaylistID}'; // Accesspoint der Spotify Web-API
  RESTRequest1.AddParameter('PlaylistID', PlaylistID);
  RESTRequest1.Execute;
  if RestResponse1.JSONValue is TJSONObject then begin
    playlist := TJson.JsonToObject<TPlaylist>(TJSONObject(RESTResponse1.JSONValue));
    if playlist <> nil then begin
      Result := playlist.Name;
      if playlist.Tracks <> nil then begin
        for track in playlist.Tracks.Items do begin
          if track.Track <> nil then begin
            List.Add(track.Track.Id);
          end;
        end;
      end;
    end;
  end;
end;

Maekkelrajter 21. Sep 2020 11:16

AW: Problem bei JSON-Abfrage
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1473923)
Ich baue mir da immer anhand der API passende Objektstrukturen zusammen. Dann wird der Abruf nicht nur wesentlich simpler, man bekommt auch gleich die Typsicherheit dazu. Ich habe hier nur einen Teil der Felder deklariert. Alle übrigen werden ignoriert. Das lässt sich relativ leicht erweitern.
Delphi-Quellcode:
uses
  System.JSON, REST.Json;

type
  TArtist = class
  private
    FName: string;
  public
    property Name: string read FName;
  end;
  ...

Vielen Dank für den ausführlichen Vorschlag. Auf eine solche wirklich elegante Lösung wäre ich als bekennender OOP-Muffel natürlich nie gekommen. Außerdem mache ich mir selten die Mühe, ein solch universelles und wiederverwendbares Konzept zu erstellen. Bei mir steht am Anfang der Wunsch nach einem bestimmten Feature, und das soll dann schnell und mit möglichst geringem (Lern-) Aufwand realisiert werden. Wenn erst mal alles läuft, gehe ich daran, den Code und das Handling zu 'optimieren', immer mit der 'Rohversion' als Rückfalloption. Wie ich schon früher schrieb, betreibe ich das Programmieren als Hobby und fast ausschließlich für den Eigenbedarf, auch gewissermaßen als 'Brain-Jogging' anstelle von Kreuzworträtseln, was in meinem Alter (73) vielleicht 'normal' wäre. Da ist der Workflow wohl etwas anders als bei einem professionellen Entwickler. Aber dennoch habe ich den Ehrgeiz, allzugroße Stümperei zu vermeiden und möglichst kompakte und strukturierte Lösungen zu finden. Da wird mich dieser Code-Happen (von einem 'Schnipsel' kann man ja wohl hier nicht sprechen) mit Sicherheit ein enormes Stück weiter bringen!

Gruß LP

Uwe Raabe 21. Sep 2020 11:40

AW: Problem bei JSON-Abfrage
 
Es sollte nicht unerwähnt bleiben, dass der Code alles andere als vollständig ist. So werden die erzeugten Objekt-Instanzen unter non-ARC Plattformen nicht wieder freigegeben. Das müsste man noch im Destructor ergänzen.

DeddyH 21. Sep 2020 11:54

AW: Problem bei JSON-Abfrage
 
Ich mache das übrigens ähnlich, nur dass das bei mir normalerweise keine Klassen, sondern Records sind. Diese verfügen über eine Klassenfunktion FromJSON, welche ein TJSONObject übernimmt und einen befüllten Record zurückgibt.

Maekkelrajter 23. Sep 2020 11:04

AW: Problem bei JSON-Abfrage
 
Seit ein paar Tagen versuche ich, eine Abfrage nach Uwe Raabes Vorschlag zu realisieren. Bei der Ausführung der ' GetPlaylistTracks' - Funktion allerdings flogen mir die AccessViolations nur so um die Ohren :(
Den 'Unfallort' konnte ich in der Procedure TJSONUnMarshal.PopulateFields in der Unit REST.JsonReflect ausmachen:
Delphi-Quellcode:
procedure TJSONUnMarshal.PopulateFields(JsonFields: TJSONObject; Data: TObject;
  JsonCustomizer: TJSONPopulationCustomizer);

[...]

        else if jsonFieldVal is TJSONArray then
        begin
          rField := GetFieldType(Data, FieldName);
          // Unmarshal TList<T>.FListHelper as dynamic array using 10.2 layout
          if (rField.FieldType.Handle = TypeInfo(System.Generics.Collections.TListHelper)) and // Hier kracht's, wenn rField = NIL
             (I < JsonFields.Count - 1) and
             (string.CompareText('listHelper', FieldName) = 0) and
             (string.CompareText('items', JsonFields.Pairs[I + 1].JsonString.Value) = 0) then
          begin
            // "listHelper":[2] - ignore
            Inc(I);
            // "items":[{},{}]
            jsonFieldVal := JsonFields.Pairs[I].JsonValue;
            LValue := rField.GetValue(Data); // Get FListHelper
            SetTListHelperValueFromArrayValue(FRTTICtx, LValue,
              function (AArrType: TRttiType): TValue
              begin
                Result := JSONToTValue(jsonFieldVal, AArrType);
              end);
            rField.SetValue(Data, LValue); // Set FListHelper
          end
          else
            SetFieldArray(Data, FieldName, TJSONArray(jsonFieldVal));
        end
Immer dann, wenn ein Array nicht deklariert ist, gibt GetFieldType(Data, FieldName) NIL zurück, worauf prompt eine AccessViolation - Exception folgt. In unserem Fall waren das die Arrays 'images' und 'available_markets'.
Deshalb habe ich nun versucht, diese in die Typ-Deklarationen einzufügen. Bei 'images' ist mir das gelungen, nicht jedoch bei 'available_markets', was laut Spotify Web API Dokumentation als 'Array of Strings' deklariert ist.
Delphi-Quellcode:
 
// Von mir hinzugefügt:
TImage = class
  private
    FHeight: Integer;
    FWidth: Integer;
    FUrl: String;
  public
    property height: Integer read FHeight;
    property width: Integer read FWidth;
    property URL: String read FUrl;
  end;


  TAlbum = class
  private
    FArtists: TArray<TArtist>;
    FMarkets: TArray<String>;
    FName: string;
  public
    property Artists: TArray<TArtist> read FArtists;
    property available_markets: TArray<string> read FMarkets; // funktioniert nicht
    property Name: string read FName;
  end;

  TTrack = class
  private
    FAlbum: TAlbum;
    FArtists: TArray<TArtist>;
    FId: string;
    FName: string;
    FMarkets: TArray<String>;
  public
    property Album: TAlbum read FAlbum;
    property Artists: TArray<TArtist> read FArtists;
    property Id: string read FId;
    property Name: string read FName;
    property available_markets: TArray<String> read FMarkets; // funktioniert nicht

   [...]

  TPlayList = class
  private
    FName: string;
    FImages: TArray<TImage>;
    FTracks: TPaging<TPlaylistTrack>;
  public
    property Name: string read FName;
    property Images: TArray<TImage> read FImages;              //das funktioniert!
    property Tracks: TPaging<TPlaylistTrack> read FTracks;
  end;
Was mache ich bei der Deklaration falsch?

Uwe Raabe 23. Sep 2020 14:24

AW: Problem bei JSON-Abfrage
 
Wenn der JSON-Value available_markets heisst, dann muss auch das Feld
Delphi-Quellcode:
FAvailable_markets
heißen. Delphi geht nach den Feldern, nicht nach den Properties. Die Regel ist: F entfernen, den dann ersten Buchstaben klein schreiben

Alternativ kann man den Namen auch mit einem Attribut überschreiben:
Delphi-Quellcode:
  TAlbum = class
  private
    FArtists: TArray<TArtist>;
    [JSONName('available_markets')]
    FMarkets: TArray<String>;
    FName: string;
  public
    property Artists: TArray<TArtist> read FArtists;
    property Markets: TArray<string> read FMarkets;
    property Name: string read FName;
  end;

Maekkelrajter 23. Sep 2020 22:02

AW: Problem bei JSON-Abfrage
 
So funktioniert's tatsächlich. Naja, das muss man natürlich wissen. Ich war eben davon ausgegangen, dass die Properties die Schnittstelle einer Klasse sind.
Dass aber ein Funktionsrückgabewert innerhalb einer Methode nicht auf Gültigkeit überprüft wird und eine Accessviolation auslöst, kann man doch nur als Bug ansehen. Ein ähnlicher Fall lag ja auch meinem Ausgangs-Posting in diesem Thread zugrunde. Dem Vernehmen nach sind ja die JSON-Bibliotheken unter Delphi 10.3 als fehlerbehaftet berüchtigt. Da kann ich nur hoffen, dass die Version 10.4 da ausgereifter ist. Aber leider ist die ja (noch) nicht als Community-Edition verfügbar. Oder habe ich da was verpasst?

Gruß LP

Maekkelrajter 26. Sep 2020 17:16

AW: Problem bei JSON-Abfrage
 
Liste der Anhänge anzeigen (Anzahl: 1)
Nachdem das Ganze prinzipiell funktionierte, tauchte prompt die nächste Hürde auf. Die Spotify Web API limitiert nämlich die Zugriffe bei einer Playlist auf 100 Einträge. Ist die Liste länger, müssen weitere 'Seiten' des Paging-Objects nachgeladen und eingelesen werden. Die Frage war also, wie die weiteren Tracks dem Playlist-Objekt, soll heißen dem Array TPlaylist.Tracks.Items hinzugefügt werden können. Ich wollte nämlich keine zusätzliche 'externe' Liste für die Einträge anlegen, sondern nach Verlassen der Prozedur 'GetPlaylistTracks' sollte das Objekt 'Playlist' den kompletten Baum des von Spotify gelieferten Playlist-Objekts zur weiteren universellen Verwendung enthalten. Anderenfalls hätte man sich den ganzen Aufwand auch sparen können. Nach vielen Stunden mühsamer Recherche und viel Try & Error habe ich folgende Lösung gefunden:
Delphi-Quellcode:
Function TSpManager.SendRequest(accesspoint:string; offset: Integer = 0; Limit: Integer = 50):TJSONValue;
begin
  result:= NIL;
  If FAuthorized Then
  begin
    RESTRequest1.Params.ParameterByName('offset').Value:= offset.ToString;
    RESTRequest1.Params.ParameterByName('limit').Value:= limit.ToString;
    RESTRequest1.Resource:= AccessPoint;
    RestRequest1.Execute;
    result:= RestResponse1.JSONValue;
  end else If NoSpotifyAccessMsg = mrYes Then AuthorizespotifyAccess;
end;


function TSpManager.GetPlaylistTracks(var Playlist: TPlaylist; PlaylistID: string): Boolean; //komplette Playlist-Daten holen
var
  JValue,oitem: TJSONValue;
  oItems: TJSONArray;
  newitem: TPlaylisttrack;
  offs,nextpos,totalItems: Integer;
begin
  result := false;
  offs:= 0;
  JValue:= SendRequest('v1/playlists/' + PlaylistID);
  if JValue is TJSONObject then
  begin
    playlist := TJson.JsonToObject<TPlaylist>(TJSONObject(JValue));
    if playlist <> nil then
    begin
      totalItems:= playlist.Tracks.total;
      offs:= length(playlist.Tracks.Items);
      while offs < TotalItems do                 // nächste 'Seite' des Paging-Objects
      begin
        Jvalue:= SendRequest('v1/playlists/' + PlaylistID + '/tracks',offs,100);
        If JValue is TJSONObject Then
        begin
          oItems := TJSONArray(TJSONObject(JValue).GetValue('items'));
         // Erweitert das Array Playlist.Tracks.Items um oItems.count und liefert die bisherige Größe als Offset zurück:
          nextpos:= playlist.IncreaseItems(oitems.count);
          for oItem in oItems do
          begin
            newitem:= TJson.JsonToObject<TPlaylisttrack>(TJSONObject(oitem));
            playlist.SetItem(newitem,nextpos); // schreibt newitem in das Array
            inc(nextpos);
          end;
        end;
        inc(offs, oitems.Count);
      end;
    end;
  end;
  result:= playlist <> NIL;
end;
Das funktioniert auch perfekt, und sogar die Performance (incl. Ladevorgang) ist akzeptabel. Aber wie immer, wenn ich so etwas zusammen gefrickelt habe, kommt die Frage auf, ob diese Lösung wirklich 'State of The Art' ist und es nicht womöglich effizienter und/oder eleganter geht :?


Gruß LP

Maekkelrajter 4. Nov 2020 10:55

AW: Problem bei JSON-Abfrage
 
Zitat:

Zitat von Maekkelrajter (Beitrag 1474130)
Dass aber ein Funktionsrückgabewert innerhalb einer Methode nicht auf Gültigkeit überprüft wird und eine Accessviolation auslöst, kann man doch nur als Bug ansehen.
Gruß LP

Nachtrag:
Dieser Bug in der Version 10.3.2 wurde schon in der Version 10.3.3 behoben.
Auf meinem Windows7 - System, auf dem ich dieses Projekt bisher entwickelt habe, läuft leider nur 10.3.2, so dass ich mich noch mit dem Bug herumschlagen musste.
Auf meinem Win10 - System läuft sowohl die IDE als auch mein Programm unter 10.3.3 fehlerfrei: Nicht deklarierte Arrays werden einfach ignoriert und lösen keine Exception mehr aus. Geht doch!

Gruß LP


Alle Zeitangaben in WEZ +1. Es ist jetzt 23:24 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