Jump to content
Marty001

Parsing JSON response from TVDB

Recommended Posts

Hi All,  I am new to JSON parsing and for some reason cannot get my head around it, I would like some assistance with parsing a multi level JSON string, I am using Delphi 10.3 natively, using the TJSONObject.  I have been able to use the TVDB API to get series information but am finding it difficult to parse the episode list that comes from the API, so far i have only had to deal with a 'single level' file but this response contains multiple records, 1 for each episode and then a nested 'array' with a list of actors per episode.  I would like some assistance with this if anybody can help.  I have tried reading the RAD STUDIO JSON documentation but soon developed a headache.  Any guidance would be appreciated.

 

I have attached a sample JSON response.

stdiscovery.json

Share this post


Link to post

First, remove the comment at the top of the stdiscovery.json file.  Comments are not valid in Json.

 

Save this as Json type unit: StJsonTypes.pas

Caveat: I assumed that fields with value NULL are Integer fields.  Since we don't have nullables, an Integer NULL becomes, you guessed it: 0.

 

unit StJsonTypes;

interface
uses
  System.Classes, System.SysUtils;

type
  TLinks = class(TObject)
  private
    Flast: Integer;
    Fprev: Integer;
    Ffirst: Integer;
    Fnext: Integer;
  public
    property first: Integer read Ffirst write Ffirst;
    property last: Integer read Flast write Flast;
    property next: Integer read Fnext write Fnext;
    property prev: Integer read Fprev write Fprev;
  end;

  TLang = class (TObject)
  private
    FepisodeName: string;
    Foverview: string;
  public
    property episodeName: string read FepisodeName write FepisodeName;
    property overview: string read Foverview write Foverview;
  end;

  TData = class (TObject)
  private
    Flanguage: TLang;
    FairsAfterSeason: Integer;
    Ffilename: string;
    FEpisodeName: string;
    FlastUpdatedBy: Integer;
    FdvdChapter: Integer;
    FthumbHeight: string;
    FairsBeforeEpisode: Integer;
    FdvdSeason: Integer;
    FthumbAdded: TDateTime;
    FdvdEpisodeNumber: Integer;
    FcontentRating: string;
    FthumbAuthor: Integer;
    FlastUpdated: Integer;
    Foverview: string;
    Fdirectors: TArray<String>;
    FairedSeason: Integer;
    FabsoluteNumber: Integer;
    FisMovie: Integer;
    Fwriters: TArray<String>;
    Fid: Integer;
    FairedEpisodeNumber: Integer;
    FthumbWidth: string;
    FairsBeforeSeason: Integer;
    FdvdDiscid: string;
    FshowUrl: string;
    FsiteRating: Double;
    FseriesId: Integer;
    FproductionCode: string;
    FfirstAired: TDateTime;
    FguestStars: TArray<String>;
    FairedSeasonID: Integer;
    FsiteRatingCount: Integer;
  public
    property id: Integer read Fid write Fid;
    property airedSeason: Integer read FairedSeason write FairedSeason;
    property airedSeasonID: Integer read FairedSeasonID write FairedSeasonID;
    property airedEpisodeNumber: Integer read FairedEpisodeNumber write FairedEpisodeNumber;
    property episodeName: string read FEpisodeName write FepisodeName;
    property firstAired: TDateTime read FfirstAired write FfirstAired;
    property guestStars: TArray<String> read FguestStars write FguestStars;
    property directors: TArray<String> read Fdirectors write Fdirectors;
    property writers: TArray<String> read Fwriters write Fwriters;
    property overview: string read Foverview write Foverview;
    property language: TLang read Flanguage write FLanguage;
    property productionCode: string read FproductionCode write FproductionCode;
    property showUrl: string read FshowUrl write FshowUrl;
    property lastUpdated: Integer read FlastUpdated write FlastUpdated;
    property dvdDiscid: string read FdvdDiscid write FdvdDiscid;
    property dvdSeason: Integer read FdvdSeason write FdvdSeason;
    property dvdEpisodeNumber: Integer read FdvdEpisodeNumber write FdvdEpisodeNumber;
    property dvdChapter: Integer read FdvdChapter write FdvdChapter;
    property absoluteNumber: Integer read FabsoluteNumber write FabsoluteNumber;
    property filename: string read Ffilename write Ffilename;
    property seriesId: Integer read FseriesId write FseriesId;
    property lastUpdatedBy: Integer read FlastUpdatedBy write FlastUpdatedBy;
    property airsAfterSeason: Integer read FairsAfterSeason write FairsAfterSeason;
    property airsBeforeSeason: Integer read FairsBeforeSeason write FairsBeforeSeason;
    property airsBeforeEpisode: Integer read FairsBeforeEpisode write FairsBeforeEpisode;
    property imdbId: string read FcontentRating write FcontentRating;
    property contentRating: string read FcontentRating write FcontentRating;
    property thumbAuthor: Integer read FthumbAuthor write FthumbAuthor;
    property thumbAdded: TDateTime read FthumbAdded write FthumbAdded;
    property thumbWidth: string read FthumbWidth write FthumbWidth;
    property thumbHeight: string read FthumbHeight write FthumbHeight;
    property siteRating: Double read FsiteRating write FsiteRating;
    property siteRatingCount: Integer read FsiteRatingCount write FsiteRatingCount;
    property isMovie: Integer read FisMovie write FisMovie;
    constructor Create;
    destructor Destroy; override;
    procedure Dump;
  end;

  TstDiscovery = class(TObject)
  private
    Flinks: TLinks;
    Fdata: TArray<TData>;
  public
    property links: TLinks read Flinks write Flinks;
    property data: TArray<TData> read Fdata write Fdata;
    constructor Create;
    destructor Destroy; override;
  end;

implementation

{ TData }

constructor TData.Create;
begin
  Flanguage := TLang.Create;
end;

destructor TData.Destroy;
begin
  Flanguage.Free;
  inherited;
end;

procedure TData.Dump;
begin
  Writeln('id:', id, ' episodeName: ', episodeName);
  Writeln('filename: ', filename);
  Writeln('firstAired: ', FormatDateTime('yyyy.mm.dd', firstAired));
  Writeln;
end;

{ TstDiscovery }

constructor TstDiscovery.Create;
begin
  FLinks := TLinks.Create;
end;

destructor TstDiscovery.Destroy;
var
  d: TData;
begin
  FLinks.Free;
  for d in data
   do d.free;
  inherited;
end;

end.

 

Save this as 'TestStDirectory.dpr' - Correct the FileDir constant to point to where you have the StDirectory.json file.

 

program TestStDirectory;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.Classes,
  System.SysUtils,
  System.RTTI,
  REST.Json,
  StJsonTypes in 'StJsonTypes.pas';

procedure TestStDiscovery;
var
  FileDir: string;
  std: TStDiscovery;
  d: TData;
  f: TStringList;
begin
  FileDir := 'D:\temp\Downloads\';
  f := TStringList.Create;
  std := nil;
  try
    Writeln('Reading file');   // Make sure to remove the // comment from the .json file - Comments are not legal in Json
    f.LoadFromFile(FileDir + 'stdiscovery.json', TEncoding.utf8);

    Writeln('Create StDiscovery object from Json');
    std := TJson.JsonToObject<TStDiscovery>(f.Text);

    Writeln(Length(std.data), ' elements found');
    for d in std.data
     do d.Dump;

    Writeln('Converting object back to Json');
    f.Text := TJson.ObjectToJsonString(std, [joIgnoreEmptyStrings, joIgnoreEmptyArrays, joDateIsUTC, joDateFormatISO8601]);

    Writeln('Saving Json to file');
    f.SaveToFile(FileDir + 'stdiscovery_recreated.json', TEncoding.utf8);

    Writeln('Done');
  finally
    f.Free;
    std.Free;
  end;
end;

begin
  try
    try
      TestStDiscovery;
    except
      on E: Exception do
        Writeln(E.ClassName, ': ', E.Message);
    end;
  finally
    Write('Press Enter: ');
    Readln;
  end;
end.

 

Output:

 

Reading file
Create StDiscovery object from Json
17 elements found
id:6143085 episodeName: The Vulcan Hello
filename: episodes/328711/6143085.jpg
firstAired: 2017.09.24

id:6143290 episodeName: Battle at the Binary Stars
filename: episodes/328711/6143290.jpg
firstAired: 2017.09.24

id:6321258 episodeName: Context Is for Kings
filename: episodes/328711/6321258.jpg
firstAired: 2017.10.01

id:6321259 episodeName: The Butcher's Knife Cares Not for the Lamb's Cry
filename: episodes/328711/6321259.jpg
firstAired: 2017.10.08

id:6353529 episodeName: Choose Your Pain
filename: episodes/328711/6353529.jpg
firstAired: 2017.10.15

id:6353530 episodeName: Lethe
filename: episodes/328711/6353530.jpg
firstAired: 2017.10.22

id:6362450 episodeName: Magic to Make the Sanest Man Go Mad
filename: episodes/328711/6362450.jpg
firstAired: 2017.10.29

id:6362451 episodeName: Si Vis Pacem, Para Bellum
filename: episodes/328711/6362451.jpg
firstAired: 2017.11.05

id:6362452 episodeName: Into the Forest I Go
filename: episodes/328711/6362452.jpg
firstAired: 2017.11.12

id:6402864 episodeName: Despite Yourself
filename: episodes/328711/6402864.jpg
firstAired: 2018.01.07

id:6452568 episodeName: The Wolf Inside
filename: episodes/328711/6452568.jpg
firstAired: 2018.01.14

id:6452569 episodeName: Vaulting Ambition
filename: episodes/328711/6452569.jpg
firstAired: 2018.01.21

id:6452570 episodeName: What's Past is Prologue
filename: episodes/328711/6452570.jpg
firstAired: 2018.01.28

id:6452571 episodeName: The War Without, the War Within
filename: episodes/328711/6452571.jpg
firstAired: 2018.02.04

id:6452573 episodeName: Will You Take My Hand?
filename: episodes/328711/6452573.jpg
firstAired: 2018.02.11

id:6854213 episodeName: Brother
filename: episodes/328711/6854213.jpg
firstAired: 2019.01.17

id:6903811 episodeName: "Will You Take My Hand?" Bonus Scene
filename: episodes/328711/6903811.jpg
firstAired: 2018.03.25

Converting object back to Json
Saving Json to file
Done
Press Enter:

 

  • Thanks 2

Share this post


Link to post

Thankyou for your reply, how should I adapt this if my JSON data is in a string and not a file, I copied the string from my VCL application into dreamweaver to format it (that is where the comment came from)

Share this post


Link to post
     std := TJson.JsonToObject<TStDiscovery>(YourJsonStringHere);

 

  • Thanks 1

Share this post


Link to post

@Lars Fosdal Isn't importing RTTI is a huge bloat to parse JSON? A bit more manual work, but for example all class properties could be read and filled from a TJSONObject(TJSONObject.ParseJSONValue(contentsting)).

  • Confused 1

Share this post


Link to post

A matter of taste.  I don't mind RTTI and we use it extensively in all our apps.

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×