How to parse json files using ISuperObject in Delphi 10 - json

I'm trying to parse a JSON file using ISuperObject. My JSON file looks like this:
{
"status":"ok",
"follows":{
"count":868,
"page_info":{
"has_previous_page":false,
"start_cursor":null,
"end_cursor":"SbXc6VJsoeTs",
"has_next_page":true
},
"nodes":[
{
"username":"username1",
"requested_by_viewer":false,
"followed_by_viewer":true,
"profile_pic_url":"URL link",
"full_name":"name",
"is_verified":false,
"id":"ID"
},
{
"username":"username2",
"requested_by_viewer":false,
"followed_by_viewer":true,
"profile_pic_url":"URL link",
"full_name":"username2",
"is_verified":false,
"id":"ID"
},
{
"username":"_username3",
"..."
]
}
I want all of the username values to be listed in a Memo.
Here is what I have tried:
var
json : ISuperObject;
row_item : ISuperObject;
begin
json := TSuperObject.ParseFile('C:\json.txt', TRUE);
for row_item in json['nodes'] do
begin
Memo1.Lines.Add(elements_itemS['usernames']);
end;
end;
I get an Access Violation on the for loop. Any ideas?

The code should look more like this instead:
var
json : ISuperObject;
node : ISuperObject;
item : IMember;
begin
json := TSuperObject.ParseFile('C:\json.txt', TRUE);
for item in json.O['follows'].A['nodes'] do
begin
node := item.AsObject;
Memo1.Lines.Add(node.S['username']);
end;
end;
Or this:
var
json : ISuperObject;
node : ISuperObject;
item : IMember;
begin
json := TSuperObject.ParseFile('C:\json.txt', TRUE);
for item in json['follows.nodes'].AsArray do
begin
node := item.AsObject;
Memo1.Lines.Add(node.S['username']);
end;
end;

Related

Marshal a Record to JSON and back [duplicate]

How can an array of record be stored in JSON via SuperObject library. For example..
type
TData = record
str: string;
int: Integer;
bool: Boolean;
flt: Double;
end;
var
DataArray: Array[0..100] of TData;
Just use the superobject Marshalling TSuperRTTIContext
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
superobject,
System.SysUtils;
type
TData = record
str : string;
int : Integer;
bool : Boolean;
flt : Double;
end;
TDataArray = Array [0 .. 100] of TData;
procedure Test;
var
DataArray : TDataArray;
so : ISuperObject;
ctx : TSuperRttiContext;
begin
ctx := TSuperRttiContext.Create;
try
so := ctx.AsJson<TDataArray>( DataArray );
finally
ctx.Free;
end;
Writeln( so.AsJson );
end;
begin
try
Test;
except
on E : Exception do
Writeln( E.ClassName, ': ', E.Message );
end;
ReadLn;
end.
Make it a string first.
Your array:
//Array[0] := 'Apple';
//Array[1] := 'Orange';
//Array[2] := 'Banana';
myArrayAsStr := '"MyArray": [{ "1": "' + Array[0] +'", "2": "' + Array[1] +'"}';
Then you can just make it into JSON with SO(myArrayAsStr)
You can always generate your array as string in a different procedure but I think thats the way to do it.
Ill keep checking if there is an easier way ;)
EDIT:
SuperObject also has the following function:
function SA(const Args: array of const): ISuperObject; overload;
You will be able to convert that to a string again and add it in the total JSON string.

Parse JSON using lkJSON

I have a JSON file which I need to parse and extract one value.
{
"user": {
"pk": 25025320,
"username": "instagram",
"full_name": "Instagram",
"is_private": false,
"profile_pic_url": "https://instagram.fmad3-5.fna.fbcdn.net/vp/295f9e76d3c26fdf613d7856e7b43348/5B4F495B/t51.2885-19/s150x150/14719833_310540259320655_1605122788543168512_a.jpg",
"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,
"url": "https://instagram.fmad3-5.fna.fbcdn.net/vp/893534d61bdc5ea6911593d3ee0a1922/5B6363AB/t51.2885-19/s320x320/14719833_310540259320655_1605122788543168512_a.jpg"
}
],
"hd_profile_pic_url_info": {
"url": "https://instagram.fmad3-5.fna.fbcdn.net/vp/8ae8a89c80ff4722eeab592b685276cb/5B5D40A1/t51.2885-19/14719833_310540259320655_1605122788543168512_a.jpg",
"width": 320,
"height": 320
},
"has_highlight_reels": true,
"auto_expand_chaining": false
},
"status": "ok"
}
I want to extract and send the value of hd_profile_pic_url_info.url to Memo2 using the lkJSON library, Instagram API and code below to achievie this result.
var
sJSON: string;
js, Items, Item: TlkJSONbase;
I, J: Integer;
begin
sJSON := Memo1.text;
js := TlkJSON.ParseText(sJSON);
for I := 0 to Pred(js.Count) do
begin
Items := js.Child[I].Field['user'];
for J := 0 to Pred(Items.Count) do
begin
Item := Items.Child[J];
Memo2.Lines.Add(VarToStr(Item.Field['hd_profile_pic_url_info'].Value));
end;
end;
end;
You forgot that hd_profile_pic_url_info is another object containing url, width, and height, so you need to go down one more time. Here is an example of how this should be done properly.
var
js : TlkJSONbase;
begin
js := TlkJSON.ParseText(Memo1.Text);
if Assigned(js) then
begin
js := js.Field['user'];
if Assigned(js) then
begin
js := js.Field['hd_profile_pic_url_info'];
if Assigned(js) then
begin
js := js.Field['url'];
if Assigned(js) then
Memo2.Lines.Add(VarToStr(js.Value));
end;
end;
end;
end;
i use this function :
function ChildValueStr(AChild: TlkJSONbase; const Name: string; DefaultValue:
AnsiString = ''): {$IF CompilerVersion>=26}WideString{$ELSE}AnsiString{$IFEND};
var
tmpIndex: integer;
tmpStr: {$IF CompilerVersion>=26}WideString{$ELSE}AnsiString{$IFEND};
begin
Result:= DefaultValue;
if IsNil(AChild) then exit;
tmpStr:= (AChild as TlkJSONobject).{$IF CompilerVersion>=26}getWideString{$ELSE}getString{$IFEND}(Name);
if tmpStr = '' then
begin
tmpIndex := (AChild as TlkJSONobject).IndexOfName(Name);
if tmpIndex <> -1 then
Result := (AChild as TlkJSONobject).getString(tmpIndex);
end
else Result:= tmpStr;
end;
so with your code, you can use it :
var sJSON: String;
js, Items, Item: TlkJSONBase;
I, J: Integer;
begin
sJSON := Memo1.text;
js := TlkJSON.ParseText(sJSON);
for I := 0 to Pred(js.Count) do
begin
Items := js.Child[I].Field['user'];
Memo2.Lines.Add(ChildValueStr(Items, 'hd_profile_pic_url_info'));
end;
end;
I hope this can help u
Another alternative to parse JSON without use external components is the free online service https://jsontodelphi.com/
You paste the JSON text and it generate a unit with delphi classes to parse the text without problems. Better if the JSON text is complicated.
In this case, the code to access to the value is simpliest:
procedure TForm2.Button1Click(Sender: TObject);
var
root:TRootClass;
begin
root := TRootClass.FromJsonString(Memo1.Lines.Text {your JSON text});
// The caption get the URL of the pic
Caption := root.user.hd_profile_pic_url_info.url;
end;

GET Value from restresponse Json

Any way to retrieve url key from this JSON?
{
"data": {
"is_silhouette": false,
"url": "https://fbcdn-profile-a.akamaihd.net/hprofile-ak-xpf1/v/t1.0-1/p200x200/13178742_10205047662871072_6233795154346712405_n.jpg?oh=194b0150c2325660390490779bf9b942&oe=57C22031&__gda__=1472746057_dc9b0557adc8408840fafb73ed325ef8"
}
}
It is provided by Facebook's Graph API. I'm using a Rest Library in Delphi 10.0 Seattle to retrieve it.
Start by reading Embarcadero's JSON documentation. You can use the classes in the System.JSON unit, eg:
uses
..., System.JSON;
var
json: string;
obj, data: TJSONObject;
url: string;
begin
json := ...; // JSON data retrieved from REST server
obj := TJSONObject.ParseJSONValue(json) as TJSONObject;
try
data := obj.Values['data'] as TJSONObject;
url := data.Values['url'].Value;
finally
obj.Free;
end;
end;
Alternatively, if you are using Embarcadero's REST client library, it can retrieve and pre-parse the JSON for you:
var
obj, data: TJSONObject;
url: string;
begin
RESTRequest1.Execute;
obj := RESTResponse1.JSONValue as TJSONObject;
data := obj.Values['data'] as TJSONObject;
url := data.Values['url'].Value;
end;

Delphi XE5 JSON nested object

I am trying to generate JSON, data source is a DB FireBird.
I have a schema to generate path like this:
schema:= TDictionary<string, string>.Create;
schema.Add('DBTableName1', 'nest1.valueKeyName1');
schema.Add('DBTableName2', 'nest1.valueKeyName2');
schema.Add('DBTableName3', 'nest2.valueKeyName1');
schema.Add('DBTableName4', 'nest3.valueKeyName1');
schema.Add('DBTableName5', 'nest3.valueKeyName2');
schema.Add('DBTableName6', 'nest4.valueKeyName1');
How to create function for generate path to make nested objects?
{
"nest1": {
valueKeyName1: DBTableValue1,
valueKeyName2: DBTableValue2,
},
"nest2": {
valueKeyName1: DBTableValue3
},
"nest5":{
"nest6": {
"key1": "value1",
"key2": "value2",
},
"nest7": {}
}
In JavaScript I can do something like:
if (object.hasOwnProperty['key'] == false) object['key'] = {};
object = object['key'];
But in Delphi I have problem, and do not know, how to go deeper:
function TKlient.wprowadzWartoscDoJSON(wartosc: string; JSON: TJSONObject; sciezka: TStringList): TJSONObject;
var
i: integer;
obiekt: TJSONObject;
para: TJSONPair;
zagniezdzen: integer;
begin
zagniezdzen := sciezka.Count - 2;
obiekt := JSON;
para:= obiekt.Get(sciezka[i]);
for i := 1 to zagniezdzen do
begin
if obiekt.Get(sciezka[i]) = nil then obiekt.AddPair(sciezka[i], TJSONObject.Create)
else obiekt := obiekt.Get(sciezka[i]);
end;
obiekt.AddPair(sciezka[sciezka.Count - 1], wartosc);
result := obiekt;
end;
Here I've used a TDictionary<string, TJSONObject> to keep track of the object paths.
var
schema: TDictionary<string, string>;
pathSchema: TDictionary<string{path to every object}, TJSONObject>;
pair: TPair<string, string>;
values: TStringDynArray;
jsonObj,
jsonChildObj,
jsonParentObj: TJSONObject;
path: string;
i: Integer;
begin
schema := TDictionary<string, string>.Create;
try
schema.Add('DBTableName1', 'nest1.valueKeyName1');
schema.Add('DBTableName2', 'nest1.valueKeyName2');
schema.Add('DBTableName3', 'nest2.valueKeyName1');
schema.Add('DBTableName4', 'nest3.valueKeyName1');
schema.Add('DBTableName5', 'nest3.valueKeyName2');
schema.Add('DBTableName6', 'nest4.valueKeyName1');
schema.Add('value1', 'nest5.nest6.key1');
schema.Add('value2', 'nest5.nest6.key2');
pathSchema := TDictionary<string, TJSONObject>.Create;
try
jsonObj := TJSONObject.Create;
try
for pair in schema do begin
values := SplitString(pair.Value, '.');
path := '';
jsonParentObj := jsonObj;
for i := Low(values) to High(values)-1 do begin
if i > 0 then
path := path + '.';
path := path + values[i];
if pathSchema.ContainsKey(path) then
jsonChildObj := pathSchema[path]
else begin
jsonChildObj := TJSONObject.Create;
jsonParentObj.AddPair(TJSONPair.Create(values[i], jsonChildObj));
pathSchema.Add(path, jsonChildObj);
end;
jsonParentObj := jsonChildObj;
end;
jsonChildObj.AddPair(TJSONPair.Create(values[High(values)], pair.Key));
end;
WriteLn(jsonObj.ToString);
finally
jsonObj.Free;
end;
finally
pathSchema.Free;
end;
finally
schema.Free;
end;
ReadLn;
end.
The above prints the following:
{
"nest4":{
"valueKeyName1":"DBTableName6"
},
"nest2":{
"valueKeyName1":"DBTableName3"
},
"nest5":{
"nest6":{
"key1":"value1",
"key2":"value2"
}
},
"nest1":{
"valueKeyName1":"DBTableName1",
"valueKeyName2":"DBTableName2"
},
"nest3":{
"valueKeyName1":"DBTableName4",
"valueKeyName2":"DBTableName5"
}
}

Read a specific value from a JSON request using SuperObject

Hello I get the next result in a web API in JSON format:
[
{
"$id":"47",
"CodISO":"BIH",
"ES":"Bosnia y Herzegovina",
"EN":"Bosnia and Herzegovina"
},
{
"$id":"48",
"CodISO":"BLR",
"ES":"Bielorrusia",
"EN":"Belarus"
},
{
"$id":"49",
"CodISO":"BLZ",
"ES":"Belice",
"EN":"Belize"
},
{
"$id":"50",
"CodISO":"BOL",
"ES":"Bolivia",
"EN":"Bolivia"
},
{
"$id":"51",
"CodISO":"BON",
"ES":"Bonaire",
"EN":"Bonaire"
},
{
"$id":"52",
"CodISO":"BOT",
"ES":"Botsuana",
"EN":"Botswana"
},
{
"$id":"53",
"CodISO":"BRA",
"ES":"Brasil",
"EN":"Brazil"
},
{
"$id":"54",
"CodISO":"BRB",
"ES":"Barbados",
"EN":"Barbados"
}
]
Now, I want read the value from item 'ES' where the value of item 'CodISO' = 'BOL' in Delphi SuperObject, I'm not able to find the solution, took all day trying it.
I don't know how iterate with SuperObject elements as I do it with Embarcadero TJSONValue, TJSONObject, TJSONArray. I'm a newbie with SuperObject:
var
json: ISuperObject;
Retriever: TIdHTTP;
Url: string;
AnsiStr: AnsiString;
begin
URL := Form1.RestClient1.BaseURL;
try
Retriever := TIdHTTP.Create(nil);
try
AnsiStr := Retriever.Get(Url);
json := SO(AnsiStr);
{ Here code to iterate with json elements in SuperObject.......
.
.
.
.
}
finally
Retriever.Free;
end;
except
on E: Exception do
ShowMessage(E.ClassName + ': ' + E.Message);
end;
End;
As Sir Rufo said, you need to read the SuperObject documentation.
Try something like this:
var
JsonArr, JsonObj: ISuperObject;
Retriever: TIdHTTP;
Url, JsonStr, ES: string;
I: Integer;
begin
URL := Form1.RestClient1.BaseURL;
try
Retriever := TIdHTTP.Create(nil);
try
JsonStr := Retriever.Get(Url);
finally
Retriever.Free;
end;
JsonArr := SO(JsonStr).AsArray;
for I := 0 to JsonArr.Length-1 do
begin
JsonObj := JsonArr.O[I];
if JsonObj.S['CodISO'] = 'BOL' then
begin
ES := JsonObj.S['ES'];
Break;
end;
end;
except
on E: Exception do
ShowMessage(E.ClassName + ': ' + E.Message);
end;
end;