XSuperObject Segmentation fault 11 Delphi XE6 - json

I downloaded the XSuperObject for reading a Json from a web server, but I get a Segmentation fault at moment where I add the Json string to the ISuperArray.
JsonResult : string;
JsonResult := IdHTTP1.Get('http://.................');
LoadJSONXSuperObject(JsonResult);
procedure TDataForm.LoadJSONXSuperObject(S: String);
var
aobj: ISuperArray;
obj2: ISuperObject;
I: Integer;
MyString: String;
begin
aobj := SA(S); // RIGHT HERE I GET THE fault (11) or bus (10)
for I := 0 to aobj.Length-1 do
begin
end;
The following code works, but it takes 2 seconds to read each record which have 17 fields and there is 800 I make the same application in Eclipse it takes 10 seconds for all 800.
try
LResult := LJsonObj.Get('d').JsonValue as TJsonObject;
LElements := LResult.Get('results').JsonValue as TJsonArray;
for i := 0 to LElements.count -1 do
begin
Try
LItem := (LElements.Get(i) as TJsonObject).Get('pbutton').JsonValue as TJsonString;
if LItem <> nil then
PButton := RemoveQuotes(LItem.ToString)
else PButton := '';
except
PButton := '';
End;
Try
LItem := (LElements.Get(i) as TJsonObject).Get('text').JsonValue as TJsonString;
if LItem <> nil then
InvText := RemoveQuotes(LItem.ToString)
else InvText := '';
except
InvText := '';
End;
Try
LItem := (LElements.Get(i) as TJsonObject).Get('buttontext').JsonValue as TJsonString;
if LItem <> nil then
ButtonText := RemoveQuotes(LItem.ToString)
else ButtonText := '';
except
ButtonText := '';
End;
end;
finally
end;
Here is a sample of the Json file.
{
"d": {
"results": [
{
"__metadata": {
"uri": "http://myserver",
"key_fields": "",
"rows_affected": -1,
"last_autoinc": 0
},
"pbutton": 1,
"text": "Pizza",
"buttontext": "Pizza",
"price1": 10.99
},
{
"__metadata": {
"uri": "http://myserver",
"key_fields": "",
"rows_affected": -1,
"last_autoinc": 0
},
"pbutton": 2,
"text": "Pizza 2",
"buttontext": "Pizza 2",
"price1": 10.99
},
{
"__metadata": {
"uri": "http://myserver",
"key_fields": "",
"rows_affected": -1,
"last_autoinc": 0
},
"pbutton": 98,
"text": null,
"buttontext": null,
"price1": 0
}
]
}
}

The sample json you provided shows that you don't get a json array ([data, data, data]), but a json object ({data}). You have to use SO(S) instead of SA(S).
uses
XSuperObject,
XSuperJSON;
procedure TDataForm.LoadJSONXSuperObject(const S: string);
var
jsonObj, currentObj: ISuperObject;
enum: TSuperEnumerator<IJSONAncestor>;
begin
jsonObj:= SO(S);
enum := jsonObj['d.results'].AsArray.GetEnumerator;
while enum.MoveNext do
begin
currentObj := enum.Current.AsObject;
PButton := currentObj.I['pbutton'];
InvText := currentObj.S['text'];
ButtonText := currentObj.S['buttontext'];
// Price := currentObj.F['price1'];
end;
end;
See also the X-SuperObject samples here: https://code.google.com/p/x-superobject/wiki/Sample

Related

Gets only the first value from the JSON array. What should I do?

※Please understand that I am not good at English, so I am using a translator to ask questions.※
You want to import and use all the values in the JSON array.
However, I completed loading after writing the code, but it seems that only the first or last value comes out. Added array number and tried everything, but did not receive any value or an error occurred.
The code to get the value in JSON array was found and used in the Stack Overflow: Delphi parse JSON array or array
The JSON code is a little different from mine, but I modified it a little and it works well. However, I am asking experts because it is absurd to bring up only the first or last value.
//============JSON CODE===============
[
{
"VehicleWeight": "3.5톤",
"AlightArea": "경남 창원시 성산구 남산동",
"Fee_Driver": "70000",
"LoadArea": "경남 함안군 산인면",
"Commission": "0",
"LiveTime": "2022-08-19 09:39:05",
"AlightDate": "2022-08-19",
"LoadDate": "2022-08-19",
"GoodName": "",
"ActionName": "취소",
"VehicleType": "카고",
"sk": "data#",
"OrderCondition": "3",
"Transport_ID": "B2682B5FC09C4CE38F2F14C84C4829D1",
"pk": "B2682B5FC09C4CE38F2F14C84C4829D1#owner",
"MultiLoading": "0",
"Shipper_Name": "한국거성화물(주)"
},
{
"VehicleWeight": "5톤축",
"AlightArea": "경남 함안군 칠서면",
"Fee_Driver": "130000",
"LoadArea": "경남 창원시 진해구 가주동",
"Commission": "0",
"LiveTime": "2022-08-19 09:00:00",
"AlightDate": "2022-08-19",
"LoadDate": "2022-08-19",
"GoodName": "목재",
"ActionName": "취소",
"VehicleType": "카고",
"sk": "data#",
"OrderCondition": "3",
"Transport_ID": "AB8D6C1ABA30497E95BD6FFF287E47A5",
"pk": "AB8D6C1ABA30497E95BD6FFF287E47A5#owner",
"MultiLoading": "0",
"Shipper_Name": "한국거성화물(주)"
},
{
"VehicleWeight": "2.5톤",
"AlightArea": "대구 달성군 유가읍",
"Fee_Driver": "90000",
"LoadArea": "경남 함안군 대산면",
"Commission": "0",
"LiveTime": "2022-08-19 11:06:24",
"AlightDate": "2022-08-19",
"LoadDate": "2022-08-19",
"GoodName": "",
"ActionName": "취소",
"VehicleType": "카고",
"sk": "data#",
"OrderCondition": "3",
"Transport_ID": "F103B2FC1A8B42C9BCB350A8041BCEA4",
"pk": "F103B2FC1A8B42C9BCB350A8041BCEA4#owner",
"MultiLoading": "0",
"Shipper_Name": "한국거성화물(주)"
},
{
"VehicleWeight": "5톤",
"AlightArea": "경남 밀양시 부북면",
"Fee_Driver": "120000",
"LoadArea": "경남 창원시 의창구 대원동",
"Commission": "0",
"LiveTime": "2022-08-19 09:00:00",
"AlightDate": "2022-08-19",
"LoadDate": "2022-08-19",
"GoodName": "",
"ActionName": "취소",
"VehicleType": "윙바디",
"sk": "data#",
"OrderCondition": "3",
"Transport_ID": "C468AAC449B343CB8CB3349CD9FD46A7",
"pk": "C468AAC449B343CB8CB3349CD9FD46A7#owner",
"MultiLoading": "0",
"Shipper_Name": "한국거성화물(주)"
},
{
"VehicleWeight": "5톤",
"AlightArea": "대전 대덕구",
"Fee_Driver": "220000",
"LoadArea": "경남 함안군 대산면",
"Commission": "0",
"LiveTime": "2022-08-19 10:03:16",
"AlightDate": "2022-08-19",
"LoadDate": "2022-08-19",
"GoodName": "",
"ActionName": "취소",
"VehicleType": "카고",
"sk": "data#",
"OrderCondition": "3",
"Transport_ID": "A89FE76963374084933E89C3BD4CB57C",
"pk": "A89FE76963374084933E89C3BD4CB57C#owner",
"MultiLoading": "0",
"Shipper_Name": "한국거성화물(주)"
},
{
"VehicleWeight": "5톤",
"AlightArea": "경북 칠곡군 약목면",
"Fee_Driver": "120000",
"LoadArea": "경북 영천시 북안면",
"Commission": "0",
"LiveTime": "2022-08-19 09:46:20",
"AlightDate": "2022-08-19",
"LoadDate": "2022-08-19",
"GoodName": "7.5미터",
"ActionName": "취소",
"VehicleType": "윙바디",
"sk": "data#",
"OrderCondition": "3",
"Transport_ID": "846A5E797D9E4A8097D816C83FE0034E",
"pk": "846A5E797D9E4A8097D816C83FE0034E#owner",
"MultiLoading": "0",
"Shipper_Name": "한국거성화물(주)"
},
{
"VehicleWeight": "5톤",
"AlightArea": "경기 화성시 서신면",
"Fee_Driver": "110000",
"LoadArea": "충남 아산시 둔포면",
"Commission": "0",
"LiveTime": "2022-08-19 11:00:00",
"AlightDate": "2022-08-19",
"LoadDate": "2022-08-19",
"GoodName": "",
"ActionName": "등록",
"VehicleType": "카고/윙바디",
"sk": "data#",
"OrderCondition": "1",
"Transport_ID": "780F2633C16542809FBB96ADADFFBA35",
"pk": "780F2633C16542809FBB96ADADFFBA35#owner",
"MultiLoading": "0",
"Shipper_Name": "한국거성화물(주)"
}
]
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ComCtrls, IdSSL , IdSSLOpenSSL, HTTPApp , IdURI,
IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdHTTP, System.JSON,
Vcl.Grids, Data.DB, Vcl.DBGrids;
type
TForm1 = class(TForm)
DateTimePicker1: TDateTimePicker;
Edit1: TEdit;
Button1: TButton;
IdHTTP1: TIdHTTP;
StringGrid1: TStringGrid;
editPK: TEdit;
Memo1: TMemo;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure StringGrid1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{Automatic column size adjustment}
procedure AutoSizeGridColumn(Grid : TStringGrid; column : integer);
var
i : integer;
temp : integer;
max : integer;
begin
max := 0;
for i := 0 to (Grid.RowCount-1) do
begin
// Among the width of each row in the specified column based on Grid Canvas
// The maximum value is determined by the width of the column
temp := Grid.Canvas.TextWidth(grid.cells[column, i]);
if temp > max then
max := temp;
end;
Grid.ColWidths[column] := max + Grid.GridLineWidth + 20;
end;
{End of autosize Column}
//*************************************************************
//==========================Get JSON array values=========================
function getData(JsonString: String; User: String; Field: String): String;
var
JSonValue: TJSonValue;
JsonArray: TJSONArray;
ArrayElement: TJSonValue;
FoundValue: TJSonValue;
begin
Result :='';
// create TJSonObject from string
JsonValue := TJSonObject.ParseJSONValue(JsonString);
// get the array
JsonArray := JsonValue as TJSONArray;
// iterate the array
for ArrayElement in JsonArray do begin
FoundValue := ArrayElement.FindValue(User);
if FoundValue <> nil then begin
Result := ArrayElement.GetValue<string>(Field);
break;
end;
end;
end;
//==========================End of getting JSON array values=========================
procedure TForm1.Button1Click(Sender: TObject);
var
idhttps: TIdHTTP;
sslIOHandler : TIdSSLIOHandlerSocketOpenSSL;
lStream : TStringStream;
JSONValue : TJSONValue;
JSONArray : TJSONArray;
JSONObject, JSONdata: TJSONObject;
JSONCount: integer;
RESULT : String;
ArrayElement: TJSonValue;
FoundValue: TJSonValue;
i : integer;
begin
Try
Result := '';
idhttps := TIdHTTP.Create();
lStream := TStringStream.Create(nil);
sslIOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
Try
sslIOHandler.SSLOptions.Method := sslvSSLv23;
sslIOHandler.SSLOptions.Mode := sslmClient;
idhttps.IOHandler := sslIOHandler;
// idhttps.Request.CustomHeaders.Add(pAuthorization);
idhttps.HandleRedirects :=False;
idhttps.Request.Method := 'GET';
idhttps.Response.ContentType := 'application/json; charset=utf-8';
idhttps.ConnectTimeout := 10000;
idhttps.ReadTimeout := 10000;
lStream.Position := 0;
idhttps.Get('insert JSON URL', lStream, []);
Finally
Result := TEncoding.UTF8.GetString(lStream.Bytes,0, lStream.Size);
JsonArray := TJSONObject.ParseJSONValue(Result) as TJSONArray;
JSONCount := JsonArray.Count;
StringGrid1.RowCount := JSONCount+1;
for i := 0 to (jsonCount)-1 do
for ArrayElement in JsonArray do begin
StringGrid1.Cells[00,i+1] := getdata(Result, '', 'ActionName');//Status
StringGrid1.Cells[02,i+1] := getdata(Result, '', 'Shipper_Name');//Company Name
StringGrid1.Cells[03,i+1] := getdata(Result, '', 'LoadDate');//Different day
StringGrid1.Cells[04,i+1] := getdata(Result, '', 'LoadArea');//to take over
StringGrid1.Cells[05,i+1] := getdata(Result, '', 'AlightArea');//Downloading
StringGrid1.Cells[06,i+1] := getdata(Result, '', 'AlightDate');//Departure Day
StringGrid1.Cells[07,i+1] := getdata(Result, '', 'VehicleType');//model
StringGrid1.Cells[08,i+1] := getdata(Result, '', 'VehicleWeight');// tonnage
StringGrid1.Cells[09,i+1] := getdata(Result, '', 'GoodName');//Cargo information
StringGrid1.Cells[10,i+1] := getdata(Result, '', 'Fee_Driver');//vehicle freight
StringGrid1.Cells[11,i+1] := getdata(Result, '', 'Commission');//commission
StringGrid1.Cells[12,i+1] := getdata(Result, '', 'sk');//pk
StringGrid1.Cells[13,i+1] := getdata(Result, '', 'pk');//sk
{if StringGrid1.Cells[13,i+1] = '300016244318#9977' then
begin
StringGrid1.Cells[01,i+1] := 'O'//연동화물
end
else
begin
StringGrid1.Cells[01,i+1] := '';//연동화물
end; }
end;
{StringGrid1 Auto-Size Cell}
for i := 0 to StringGrid1.ColCount-1 do
begin
AutoSizeGridColumn(StringGrid1, i);
end;
{StringGrid1 Auto-Size Cell END}
FreeAndNil(lStream);
FreeAndNil(idhttps);
FreeAndNil(sslIOHandler);
End;
except
on E: EIdHTTPProtocolException do
begin
Result := e.ErrorMessage;
FreeAndNil(idhttps);
FreeAndNil(sslIOHandler);
end;
End;
end;
end.
I see a lot of problems in this code.
In Button1Click(), the outer for loop is unnecessary and should be removed. Between the two for loops, you are re-looping through the same array over and over needlessly. The inner for..in loop will suffice.
Also, it doesn't make sense to have getValue() take the original JSON as a string, as Button1Click() has already parsed it out, just to have getValue() reparse it again again and again. Lots of wasted overhead. You should pass the existing TJSONArray or even the ArrayElement to getValue() (or better, just eliminate getValue() completely and move its logic into Button1Click directly, especially since your call to FindValue(User) doesn't make sense as User is always blank).
Also, getData() and Button1Click() are both leaking the TJSONValue objects that ParseJSONValue() returns.
Also, you should be using the overload of TIdHTTP.Get() that returns a string instead of fills a TStream.
Also, your try..finallys need some cleanup, you are not adequately protecting all of your objects from errors.
With all of that said, try something more like this instead:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ComCtrls, IdSSL , IdSSLOpenSSL, HTTPApp , IdURI,
IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdHTTP, System.JSON,
Vcl.Grids, Data.DB, Vcl.DBGrids;
type
TForm1 = class(TForm)
DateTimePicker1: TDateTimePicker;
Edit1: TEdit;
Button1: TButton;
IdHTTP1: TIdHTTP;
StringGrid1: TStringGrid;
editPK: TEdit;
Memo1: TMemo;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure StringGrid1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{Automatic column size adjustment}
procedure AutoSizeGridColumn(Grid : TStringGrid; Column : integer);
var
i : integer;
temp : integer;
max : integer;
begin
max := 0;
for i := 0 to Grid.RowCount-1 do
begin
// Among the width of each row in the specified column based on Grid Canvas
// The maximum value is determined by the width of the column
temp := Grid.Canvas.TextWidth(Grid.Cells[Column, i]);
if temp > max then
max := temp;
end;
Grid.ColWidths[Column] := max + Grid.GridLineWidth + 20;
end;
{End of autosize Column}
//*************************************************************
procedure TForm1.Button1Click(Sender: TObject);
var
idhttps: TIdHTTP;
sslIOHandler : TIdSSLIOHandlerSocketOpenSSL;
JSONValue : TJSONValue;
JSONArray : TJSONArray;
ArrayElement: TJSONValue;
i : integer;
begin
idhttps := TIdHTTP.Create;
try
sslIOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(idhttps);
sslIOHandler.SSLOptions.Mode := sslmClient;
sslIOHandler.SSLOptions.Method := sslvSSLv23;
idhttps.IOHandler := sslIOHandler;
// idhttps.Request.CustomHeaders.Add(pAuthorization);
idhttps.HandleRedirects := False;
idhttps.ConnectTimeout := 10000;
idhttps.ReadTimeout := 10000;
idhttps.HTTPOptions := idhttps.HTTPOptions + [hoNoProtocolErrorException, hoWantProtocolErrorContent];
Result := idhttps.Get('insert JSON URL');
finally
idhttps.Free;
end;
JSONValue := TJSONObject.ParseJSONValue(Result);
try
JsonArray := JSONValue as TJSONArray;
StringGrid1.RowCount := JsonArray.Count + 1;
i := 1;
for ArrayElement in JsonArray do
begin
StringGrid1.Cells[00,i] := ArrayElement.GetValue<string>('ActionName','');//Status
StringGrid1.Cells[02,i] := ArrayElement.GetValue<string>('Shipper_Name','');//Company Name
StringGrid1.Cells[03,i] := ArrayElement.GetValue<string>('LoadDate','');//Different day
StringGrid1.Cells[04,i] := ArrayElement.GetValue<string>('LoadArea','');//to take over
StringGrid1.Cells[05,i] := ArrayElement.GetValue<string>('AlightArea','');//Downloading
StringGrid1.Cells[06,i] := ArrayElement.GetValue<string>('AlightDate','');//Departure Day
StringGrid1.Cells[07,i] := ArrayElement.GetValue<string>('VehicleType','');//model
StringGrid1.Cells[08,i] := ArrayElement.GetValue<string>('VehicleWeight','');// tonnage
StringGrid1.Cells[09,i] := ArrayElement.GetValue<string>('GoodName','');//Cargo information
StringGrid1.Cells[10,i] := ArrayElement.GetValue<string>('Fee_Driver','');//vehicle freight
StringGrid1.Cells[11,i] := ArrayElement.GetValue<string>('Commission','');//commission
StringGrid1.Cells[12,i] := ArrayElement.GetValue<string>('sk','');//pk
StringGrid1.Cells[13,i] := ArrayElement.GetValue<string>('pk','');//sk
{if StringGrid1.Cells[13,i] = '300016244318#9977' then
begin
StringGrid1.Cells[01,i] := 'O'//연동화물
end else begin
StringGrid1.Cells[01,i] := '';//연동화물
end;}
Inc(i);
end;
finally
JSONValue.Free;
end;
{StringGrid1 Auto-Size Cell}
for i := 0 to StringGrid1.ColCount-1 do
begin
AutoSizeGridColumn(StringGrid1, i);
end;
{StringGrid1 Auto-Size Cell END}
end;
end.

How to parse this type of JSON data in Delphi?

How do I parse the following Json in Delphi?
This is my first post, and I've taken care to search as much as I can before asking this question,
so please kindly let me know if I posted wrongly in any way.
I would like to get the value of "name_of_centre" in the "records" array
Thank you for any kind assistance.
procedure TForm1.Button1Click(Sender: TObject);
var
i : integer;
jsonRoot: TJSONValue;
jsonObj: TJSONObject;
jsonArr: TJSONArray;
begin
jsonRoot := TJSONObject.ParseJSONValue(memo2.Lines.text);
try
jsonObj := jsonRoot as TJSONObject;
jsonObj := jsonObj.GetValue('result') as TJSONObject;
jsonArr := jsonObj.GetValue('records') as TJSONArray;
showmessage( jsonArr.Count.ToString ); // works ok
for i := 0 to jsonArr.Count - 1 do
begin
jsonObj := jsonArr.Items[i] as TJSONObject;
showmessage( jsonObj.GetValue('name_of_centre').Value ); // error here
end;
finally
jsonRoot.Free;
end;
end;
I've checked out
Delphi parsing a Json with multiple array types?
How to parse this json data in Delphi 10 Seattle?
(especially this one)
and a few other links... but the JSON format seems different.
Any advice?
{
"help": "testing",
"success": true,
"result": {
"resource_id": "data_resource",
"fields": [
{
"type": "int4",
"id": "_id"
},
{
"type": "text",
"id": "name_of_centre"
},
{
"type": "text",
"id": "location_of_centre"
},
{
"type": "text",
"id": "type_of_centre"
},
{
"type": "text",
"id": "owner"
},
{
"type": "numeric",
"id": "no_of_outlets"
},
{
"type": "numeric",
"id": "no_of_branches"
}
],
"records": [
{
"location_of_centre": "Kings Road",
"no_of_outlets": "12",
"no_of_branches": "0",
"name_of_centre": "Kings Road Centre",
"type_of_centre": "HC",
"owner": "Private",
"_id": 1
},
{
"location_of_centre": "Queens",
"no_of_outlets": "14",
"no_of_branches": "1",
"name_of_centre": "Queens Centre",
"type_of_centre": "HC",
"owner": "Public",
"_id": 2
}
],
"_links": {
"start": "ignore",
"next": "ignore2"
},
"limit": 2,
"total": 10
}
}
Thanks for the many replies.
Olivier : I've included the error in this amended code.
Peter : I tried using
jsonRoot.GetValue('result.records[0].name_of_centre')
and it does give me the value of name_of_centre. Good start.
But I'm hoping to get this code to give me the number of items in Array and I iterate the array, instead of hard-code. Thanks.
Remy : strangely though, it works today. It doesn't get Invalid Typecast
at showmessage( jsonObj.GetValue('name_of_centre').Value );
fpiette : I use Delphi 10.3 RIO.
Thanks to everyone for replying.
Is there a need to use jsonRoot.Free; -- i saw this in a posting on stackoverflow.com... How about jsonObj.Free?
jsonRoot := TJSONObject.ParseJSONValue(memo2.Lines.text);
try
jsonObj := jsonRoot as TJSONObject;
jsonObj := jsonObj.GetValue('result') as TJSONObject;
showmessage( jsonObj.ToString );
jsonArr := jsonObj.GetValue('records') as TJSONArray;
showmessage( jsonArr.Count.ToString );
for i := 0 to jsonArr.Count - 1 do
begin
jsonObj := jsonArr.Items[i] as TJSONObject;
if jsonObj .GetValue('name_of_centre').Value = null then
showmessage('null');
// previously had an Invalid Typecast
showmessage( jsonObj.GetValue('name_of_centre').Value );
end;
finally
jsonRoot.Free;
end;

LkJSON jsList parsing Problem in Delphi 7

I'm using LkJSON v1.07 to parse JSON in Delphi 7, converting it to TTreeVeiw.
I use this JSON sample from the Internet:
{
"user": {
"pk": 25025320,
"username": "instagram",
"full_name": "Instagram",
"is_private": false,
"profile_pic_id": "1360316971354486387_25025320",
"is_verified": true,
"has_anonymous_profile_picture": false,
"media_count": 5164,
"follower_count": 233901016,
"following_count": 187,
"biography": "Discovering — and telling — stories from around the world.",
"external_url": "",
"usertags_count": 144,
"hd_profile_pic_versions": [
{
"width": 320,
"height": 320,
}
],
"hd_profile_pic_url_info": {
"width": 320,
"height": 320
},
"has_highlight_reels": true,
"auto_expand_chaining": false
},
"status": "ok" ,
}
Here is the relevant Delphi code:
procedure TForm1.PopulateTree;
var
jsDoc : TlkJSONbase;
begin
jsDoc := TlkJSON.ParseText(JSONMemo.Text);
if Assigned(jsDoc) then
begin
JsonTree.Items.Clear;
DomToTree(jsDoc, nil);
end;
end;
procedure TForm1.DomToTree(JsonNode: TlkJSONbase; TreeNode: TTreeNode);
var
I,j: Integer;
NewTreeNode: TTreeNode;
NodeText: string;
jsObj: TlkJSONobject;
jsLst: TlkJSONlist;
n: integer;
begin
case JsonNode.SelfType of
jsObject : begin
jsObj := (JsonNode AS TlkJSONobject);
NodeText := jsObj.NameOf[0] + '[' + jsObj.SelfTypeName + ']';
for I := 0 to jsObj.Count - 1 do
begin
N := jsObj.FieldByIndex[i].Count;
if n > 0 then
NodeText := Format('%s [ %s ] [ %d ]', [jsObj.Nameof[i], jsObj.FieldByIndex[i].SelfTypeName , jsObj.FieldByIndex[i].Count])
else
NodeText := Format('[%s] %s = %s', [jsObj.FieldByIndex[i].SelfTypeName, jsObj.Nameof[i], jsObj.FieldByIndex[i].Value]);
NewTreeNode := JsonTree.Items.AddChild(TreeNode,NodeText);
DomToTree(jsObj.FieldByIndex[i], NewTreeNode);
end;
end;
jsList : begin
jsLst := (JsonNode AS TlkJSONlist);
for I := 0 to jsLst.Count - 1 do
begin
NodeText := jsLst.getString(i);
NewTreeNode := JsonTree.Items.AddChild(TreeNode, NodeText);
end;
end;
jsBase :;
jsNumber :;
jsString :;
jsBoolean : ;
jsNull : ;
end;
end;
The problem is, it does not display the items of the "hd_profile_pic_versions" node, which is identified as jsList. It just displays its name but not the contents, and gives an Invalid Class Typecast error message.
Edit
A screenshot of the application for the biography anomaly

Identifying JSON level with Delphi

I want to parse a small JSON file with a nested structure. I am mainly interested in the "name" value, but as there are several name values throughout the document, it would be convenient to have a level depth identifier of some kind.
{
"status": "Tomato",
"name": "ThisIsWhatIwant",
"params": [
{
"name": "THatsNoGood",
"values": [
{
"value": ""
}
]
},
{
"name": "dontlikeiteither",
"values": [
{
"value": ""
}
]
},
{
"name": "Pffff",
"values": [
{
"value": ""
}
]
},
{
"name": "Trump",
"values": [
{
"value": ""
}
]
},
{
"name": "Obama",
"values": [
{
"value": ""
}
]
},
{
"name": "Jackson5",
"values": [
{
"value": ""
}
]
}
],
"NewEden": false,
"Potatoes": []
}
]
Delphi code:
procedure TFmain.json_extract_names(filename: string);
var jsonStr: string;
sr: TStringReader; jtr: TJsonTextReader; s: string;
sl: TSTringList;
I: Integer;
begin
jsonStr := TFile.ReadAllText(FileOpenDialog1.FileName);
sl := Tstringlist.Create;
sr := TStringReader.Create(jsonStr);
try
jtr := TJsonTextReader.Create(sr);
try
while jtr.Read do
begin
s := JsonTokenToString(jtr.TokenType);
if jtr.TokenType = TJsonToken.PropertyName then
begin
if jtr.Value.ToString = 'name' then
begin
jtr.Read;
sl.Add(jtr.Value.AsString);
end
else if jtr.TokenType = TJsonToken.EndObject then
begin
exit;
end;
end;
end;
finally
jtr.Free;
end;
for I := 0 to sl.Count-1 do
begin
ComboBoxsearch.Items.Add(sl[i]);
end;
finally
sr.Free;
sl.Free;
end;
End;
The above code works and my stringlist contains all name values - but I only need the name of the first level! Is there any way to only get the first level name? (in my example JSON the desired result would be: ThisIsWhatIwant)
TJsonTextReader has a Depth property:
Gets the depth of the current token in the JSON document.
Depth returns an integer that represents the nested level of the current token.
For example:
procedure TFmain.json_extract_names(filename: string);
var
jsonStr: string;
sr: TStringReader;
jtr: TJsonTextReader;
sl: TStringList;
begin
jsonStr := TFile.ReadAllText(filename);
sl := TStringList.Create;
try
sr := TStringReader.Create(jsonStr);
try
jtr := TJsonTextReader.Create(sr);
try
while jtr.Read do
begin
if (jtr.Depth = 1) and
(jtr.TokenType = TJsonToken.PropertyName) and
(jtr.Value.ToString = 'name') then
begin
jtr.Read;
sl.Add(jtr.Value.AsString);
end;
end;
finally
jtr.Free;
end;
finally
sr.Free;
end;
ComboBoxsearch.Items.AddStrings(sl);
finally
sl.Free;
end;
End;

Json and System.JSON in Delphi

I have this Json
{
"Sucess": true,
"Msg": "OK",
"Ret": {
"First": 0,
"Next": true,
"Total": 60,
"Itens": [
{
"ID": 212121,
"Name": "uuuuuuuuuuuuuuuuuuuuuuuu",
"LcID": 9898,
"Oclao": false,
"Lal": {
"ID": 12202,
"Name": "pppppppppppppppppp",
"Pais": "Brasil",
"Dtc": 0.0
},
"Subtipo": {
"ID": 7458,
"Desc": "mnmnmnmnn"
},
"Tipo": {
"Sit": "cor1",
"Sitrm": 0,
"Name": "Shsdfow"
},
"Qtde": 0,
"Qntcoes": 0,
"Pubum": "adfsdfsdfs",
"Evias": {
"arq": {
"Mo": [
"site.com"
],
"Moir": [
"site.com"
]
}
}
},
{
"ID": 9797878,
"Name": "uuuuuuuuuuuuuuuuuuuuuuuu",
"LcID": 9898,
"Oclao": false,
"Lal": {
"ID": 12332,
"Name": "pppppppppppppppppp",
"Pais": "Brasil",
"Dtc": 0.0
},
"Subtipo": {
"ID": 7458,
"Desc": "mnmnmnmnn"
},
"Tipo": {
"Sit": "cor1",
"Sitrm": 0,
"Name": "Shsdfow"
},
"Qtde": 0,
"Qntcoes": 0,
"Pubum": "adfsdfsdfs",
"Evias": {
"arq": {
"Mo": [
"site.com"
],
"Moir": [
"site.com"
]
}
}
}
]
}
}
however, I can only take the values of the first items "Sucess, Msg and Ret"
I'm doing as follows
var
JSONValue, jv: TJSONValue;
joName: TJSONObject;
data: TBytes;
sHtmlResp, sTemp : String;
begin
sHtmlResp := '[' + sHtmlResp + ']';
data := TEncoding.ASCII.GetBytes(sHtmlResp);
JSONValue := TJSONObject.ParseJSONValue(data, 0);
for jv in JSONValue as TJSONArray do
begin
joName := jv as TJSONObject;
sTemp:= joName.Get('Msg').JSONValue.Value;
end;
end;
sHtmlResp contains the string json.
I've tried some other ways to get the most unsuccessful field, how do I get the sub-items as "Ret", "Items" and so on.
A detail, had to add '[' ']' in the JSON string I get, otherwise I can not get even the first fields.
Thank!
Ret is a subobject so you need to access it as such. It has its own values and an array of subobjects, and so on.
Try this:
var
joName, joRet, joItem: TJSONObject;
joItems: TJSONArray;
sHtmlResp, sMsg: String;
bSuccess: Boolean;
begin
sHtmlResp := ...; // original JSON without added braces around it
joName := TJSONObject.ParseJSONValue(sHtmlResp) as TJSONObject;
bSuccess := joName.GetValue('Success') is TJSONTrue;
// if you are using Delphi 10 Seattle or later, you can use this instead:
// bSuccess := (joName.GetValue('Success') as TJSONBool).AsBoolean;
sMsg := joName.GetValue('Msg').Value;
joRet := joName.GetValue('Ret') as TJSONObject;
// use joRet.GetValue() as needed ...
joItems := joRet.GetValue('Itens') as TJSONArray;
for i := 0 to joItems.Count-1 do
begin
joItem := joItems.Items[i] as TJSONObject;
// use joItem.GetValue() as needed ...
end;
end;
If each object/sub-object have unique ObjectID (name).
The simple way is using this function
function ExtractJsonParm(parm:string;h:string):string;
var r:integer;
begin
r:=pos('"'+Parm+'":',h);
if r<>0 then
result := copy(h,r+length(Parm)+4,pos(',',copy(h,r+length(Parm)+4,length(h)))-1)
else
result:='';
end;
Usage:
begin
Writeln(ExtractJsonParm('First',Value));
Writeln(ExtractJsonParm('Next',Value));
Writeln(ExtractJsonParm('Total',Value));
end;
Outputs:
0
true
60
Using jsonDoc it would be
JSON(sHtmlResp)['Msg']
optionally enclosed in VarToStr to enforce type (and also silently convert Null to empty string value).
As noted in the comments by Remy: no need to enclose in [] as this enforces the outer array.