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;
Related
I want to parse the JSON and extract all ID values and assign, show them in a combo box.
I am using ULKJson library and Delphi 7. Here is my JSON
{
"ID": null,
"ResCode": 100,
"ResMessage": "هیچ آیتم جدیدی در پایگاه داده ذخیره نشد",
"ResPos": null,
"ResData": {
"data": [
{
"data": {
"id": "2994LUZUWL",
"type": "SHEBAD_ID"
},
"sendDate": "2021-11-02T20:00:16.827"
},
{
"data": {
"id": "2992CQVAE1",
"type": "SHEBAD_ID"
},
"sendDate": "2021-11-02T18:43:12.857"
},
{
"data": {
"id": "Z2005KUU6ZB",
"type": "SHEBAD_ID"
},
"sendDate": "2021-11-02T18:51:36.107"
},
{
"data": {
"id": "Z29914MM2WL",
"type": "SHEBAD_ID"
},
"sendDate": "2021-11-02T19:21:08.607"
}
],
"message": "",
"succeeded": true
}
}
How can I access to ID values inside this JSON?
Delphi 6 user here, but same thing really..
You need to have uLKJson installed, which I assume you have.
You need to parse each element as one of the uLKJSON types:
jsBase, jsNumber, jsString, jsBoolean, jsNull, jsList, jsObject
You have
{ <<< root = object
"ResData": { <<< ResData = object
"data": [ <<< data = list
{ <<< element[0] = object
"data": { <<< data = object
"id": "2994LUZUWL", <<< id = string. This is what you want.
...
In jq, this would be selection string: ".ResData.data[].data.id".
The program to parse the data could look something like this:
program testparse;
{$APPTYPE CONSOLE}
uses
SysUtils, // for writing on console
ulkjson; // to parse JSON
var
vJsonObj: TlkJsonObject;
jo : TlkJSONstreamed;
listofelements: TlkJSONlist;
i : integer;
// {
// "data": {
// "id": "2994LUZUWL",
// "type": "SHEBAD_ID"
// },
// "sendDate": "2021-11-02T20:00:16.827"
// },
// function for simplicity
//
function get_key( j : TlkJSONobject) : string;
begin
try
result := j.Field['data'].Field['id'].Value;
except
result := 'N/A'; // if the id key is not there..
end;
end;
begin
jo := TlkJSONstreamed.Create;
try
vJsonObj := jo.LoadFromFile('jsontext.txt') as TlkJsonObject;
listofelements := vJsonObj.Field['ResData'].Field['data'] as TlkJSONlist;
for i := 0 to listofelements.Count-1 do
begin
writeln('Key no ' + inttostr(i) + '=' +
get_key(listofelements.Child[i] as TlkJSONobject)
);
end;
finally
jo.free;
end;
end.
Result:
Key no 0=2994LUZUWL
Key no 1=2992CQVAE1
Key no 2=Z2005KUU6ZB
Key no 3=Z29914MM2WL
Alternative library
Alternatively JSONTools, originally from Lazarus can be used. I have modified and uploaded a version for Delphi 6/7.
Using this unit:
writeln('-- doing it with JSONTools');
N := TJsonNode.Create;
n.LoadFromFile('jsontext.txt'); // will automatically do a parse
n:= n.Find('/ResData/data'); // get the array
for i := 0 to n.Count-1 do
begin
writeln(
'Key no ' + inttostr(i) + '=' +
n.Child(i).Find('data/id').AsString);
end;
n.Free;
Adding to a combobox
As the question was for how to add to a combobox: this is simple. First clear the combobox, then for each item do an "additem".
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;
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
I have a JSON file that looks like this and I'm trying to decode it, but with no luck:
[
{
"FirstName": "Kim",
"Surname": "Jensen"
},
{
"FirstName": "Amery",
"Surname": "Mcmillan"
},
{
"FirstName": "Denton",
"Surname": "Burnett"
}
...
]
Using uJson with Delphi 2007, I know how to extract the data when the array has a name like this:
{
"Names": [
{
"FirstName": "Kim",
"Surname": "Jensen"
},
{
"FirstName": "Amery",
"Surname": "Mcmillan"
},
{
"FirstName": "Denton",
"Surname": "Burnett"
}
...
]
}
var
json: TJSONObject;
Text: String;
i: Integer;
begin
json := TJSONObject.create(jsontext);
for i:=0 to json.getJSONArray('Names').Length -1 do
begin
Text := json.getJSONArray('Names').getJSONObject(i).optString('FirstName');
...
end;
end;
But, this array has no name, and I have tried almost everything I can think of and still this simple thing has taking me hours to figure out.
In the JSON you are having trouble with, the top-level data is the array, so you need to parse it using TJSONArray instead of TJSONObject.
var
json: TJSONArray;
Text: String;
i: Integer;
begin
json := TJSONArray.create(jsontext);
try
for i := 0 to json.Length-1 do
begin
Text := json.getJSONObject(i).optString('FirstName');
...
end;
finally
json.Free;
end;
end;
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.