Marty001 0 Posted July 14, 2020 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
Lars Fosdal 1792 Posted July 15, 2020 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: 2 Share this post Link to post
Marty001 0 Posted July 15, 2020 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
Lars Fosdal 1792 Posted July 15, 2020 std := TJson.JsonToObject<TStDiscovery>(YourJsonStringHere); 1 Share this post Link to post
aehimself 396 Posted July 15, 2020 @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)). 1 Share this post Link to post
Lars Fosdal 1792 Posted July 15, 2020 A matter of taste. I don't mind RTTI and we use it extensively in all our apps. Share this post Link to post