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;
Related
This works, when it is an object:
{"data":{"url":"stackoverflow.com"}}
This does not work, when it is an array:
{"data":[{"url":"stackoverflow.com"}]}
Error: Invalid class typecast
procedure TForm1.Button1Click(Sender: TObject);
var
json : string;
obj, data : TJSONObject;
url : string;
begin
json := '{"data":{"url":"stackoverflow.com"}}';
obj := TJSonObject.ParseJSONValue(json) as TJSONObject;
try
data := obj.Values['data'] as TJSONObject;
url := data.Values['url'].value;
showMessage(url);
finally
obj.Free;
end;
end;
I know I have to use TJSONArray, but I don't know how to implement it.
This is not hard to understand.
In the first case, the value of data is an object, so obj.Values['data'] as TJSONObject is correct.
In the second case, the value of data is an array, so obj.Values['data'] as TJSONObject is wrong. it needs to be obj.Values['data'] as TJSONArray instead, and then you access the TJSONObject from the elements of the array, eg:
procedure TForm1.Button1Click(Sender: TObject);
var
json : string;
obj, data : TJSONObject;
arr: TJSONArray;
url : string;
begin
json := '{"data":[{"url":"stackoverflow.com"}]}';
obj := TJSonObject.ParseJSONValue(json) as TJSONObject;
try
arr := obj.Values['data'] as TJSONArray;
data := arr[0] as TJSONObject;
url := data.Values['url'].Value';
ShowMessage(url);
finally
obj.Free;
end;
end;
I have a JSON string which comes from Amazon. (https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/index.json) The top of the JSON looks like this...
I need to retrieve a list of JUST the offerCode and versionIndexUrl values. I can read an array of offers, but the key of each offer is different, so I can't use the NAME(comprehend, AmazonMWAA, etc). I have tried using element [0] but I get an AV. Here is my relevant code...
procedure Load_AWS_Services;
var
json: string;
idx: Integer;
obj: TJSONObject;
j_array: TJSONArray; // the array of all lineitems/offers
lineItem : TJSONObject;
ServiceEntry: TJSONPair;
begin
try
JSON := DownloadFromURLasString('https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/index.json');
obj := TJSONObject.ParseJSONValue(JSON) as TJSONObject;
try
// Now parse the data...
j_array := TJSONArray(obj.Get('offers').JsonValue);
// Now loop through each individual item
for idx := 0 to pred(j_array.size) do
begin
lineItem := TJSONObject(j_array.Get(idx));
Main.Memo1.Lines.Add(lineItem.ToString); // this shows each offer...so good to this point
ServiceEntry := lineItem.Pairs[0];
ShowMessage(ServiceEntry.Value); // AV here
What do I need to change in my last 2 lines to read INSIDE the 'offers'?
The offers field is a JSON object, not a JSON array (had you used the as operator for the TJSONArray cast, you would have gotten an EInvalidCast exception raised).
Try this instead:
procedure Load_AWS_Services;
var
json: string;
idx: Integer;
j_val: TJSONValue;
j_obj: TJSONObject;
j_pair: TJSONPair;
offers: TJSONObject;
lineItem : TJSONObject;
begin
try
json := DownloadFromURLasString('https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/index.json');
j_val := TJSONObject.ParseJSONValue(json);
try
j_obj := j_val as TJSONObject;
// Now parse the data...
offers := j_obj.GetValue('offers') as TJSONObject;
// Now loop through each individual item
for idx := 0 to pred(offers.Count) do
begin
j_pair := offers.Get(idx);
Main.Memo1.Lines.Add(j_pair.JsonString.Value);
lineItem := j_pair.JsonValue as TJSONObject;
ShowMessage(lineItem.GetValue('offerCode').Value);
ShowMessage(lineItem.GetValue('versionIndexUrl').Value);
...
end;
finally
j_val.Free;
end;
except
...
end;
end;
I have a fairly simple JSON:
[
{
"hero-img": "girl-ipad.png",
"main-story-headline": "How to Stay Mentally Healthy During a Pandemic",
"main-story-paragraph": "lorem ipsum",
"main-story-button-text": "Read More"
},
{
"hero-img": "painter-woman.png",
"main-story-headline": "How to Stay Mentally Healthy During a Pandemic",
"main-story-paragraph": "lorem ipsum",
"main-story-button-text": "Explore More"
},
{
"hero-img": "old-man.png",
"main-story-headline": "How to Stay Mentally Healthy During a Pandemic",
"main-story-paragraph": "lorem ipsum",
"main-story-button-text": "Get Yours Now"
} ]
By using Delphi 10.3 Rio, I want to iterate through all properties no matter how they are named. I've started this way:
program JuTemplates;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, System.JSON, System.IOUtils, System.Types;
var
root, OutputDir, TemplatesDir, JsonDir, jsonText: string;
JsonFiles: TStringDynArray;
i, j: Integer;
JSONValue: TJSONObject;
JSONValues: TJSONArray;
begin
try
try
root := ExtractFilePath(ParamStr(0));
OutputDir := root + 'OutputDir\';
TemplatesDir := root + 'TemplatesDir\';
JsonDir := root + 'JsonDir\';
writeln('Processing: ' + JsonDir);
JsonFiles := TDirectory.GetFiles(JsonDir);
for i := 0 to High(JsonFiles) do
begin
jsonText := TFILE.ReadAllText(JsonFiles[i]);
JSONValues := TJSONObject.ParseJSONValue(jsonText) as TJSONArray;
for j := 0 to JSONValues.Count - 1 do
begin
JSONValue := JSONValues.Items[i] as TJSONObject;
// here I should iterate through that JSONValue object
end;
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
finally
writeln ('Press any key to continue');
readln;
end;
end.
To enumerate a JSONObject, you can use an enumerator like below.
Use for..in loop (implicit enumerator)
procedure TForm1.Button1Click(Sender: TObject);
var
JSONData : String;
JSONObject : TJSONObject;
JSONPair : TJSONPair;
begin
JSONData := '... some JSON data ...'; // Place you data here
JSONObject := TJSonObject.ParseJSONValue(JSONData) as TJSONObject;
try
for JSONPair in JSONObject do
ShowMessage(Format('1) Key=%s Value=%s',
[JSONPair.JsonString.ToString,
JSONPair.JsonValue.ToString]));
finally
JSONObject.Free;
end;
end;
Use a classic for loop:
procedure TForm1.Button1Click(Sender: TObject);
var
JSONData : String;
JSONObject : TJSONObject;
JSONPair : TJSONPair;
I : Integer;
begin
JSONData := '... some JSON data ...'; // Place you data here
JSONObject := TJSonObject.ParseJSONValue(JSONData) as TJSONObject;
try
for I := 0 to JSONObject.Count - 1 do begin
ShowMessage(Format('Key=%s Value=%s',
[JSONObject.Pairs[I].JsonString.ToString,
JSONObject.Pairs[I].JsonValue.ToString]));
end;
finally
JSONObject.Free;
end;
end;
Use an explicit enumerator and a while loop:
procedure TForm1.Button1Click(Sender: TObject);
var
JSONData : String;
JSONObject : TJSONObject;
JSONEnumerator : TJSONObject.TEnumerator;
JSONPair : TJSONPair;
begin
JSONData := '... some JSON data ...'; // Place you data here
JSONObject := TJSonObject.ParseJSONValue(JSONData) as TJSONObject;
try
JSONEnumerator := JSONObject.GetEnumerator;
try
while JSONEnumerator.MoveNext do begin
JSONPair := JSONEnumerator.Current;
ShowMessage(Format('Key=%s Value=%s',
[JSONPair.JsonString.ToString,
JSONPair.JsonValue.ToString]));
end;
finally
JSONEnumerator.Free;
end;
finally
JSONObject.Free;
end;
end;
Note that you may need to check if JSONPair.JsonValue has childs and enumerate those childs with another enumerator, recursively.
I'm trying to get JSON data from an URL. The site I'm trying to connect is:
http://www.bitven.com/assets/js/rates.js
It contains the following JSON string:
{
"USD_TO_BSF_RATE": 112268.29,
"BTC_TO_USD_RATE": 13870.9,
"ETH_TO_USD_RATE": 752.222,
"BCH_TO_USD_RATE": 2960.81,
"LTC_TO_USD_RATE": 272.476,
"XRP_TO_USD_RATE": 1.01954,
"ETC_TO_USD_RATE": 31.1101,
"DASH_TO_USD_RATE": 1178.0,
"ZEC_TO_USD_RATE": 561.377,
"XMR_TO_USD_RATE": 354.709
}
I need to get the value of USD_TO_BSF_RATE, which is updated every 5 minutes in the site I mentioned. My code looks like:
uses
... System.JSON, IdHTTP;
function GetUrlContent(s: string): string;
var
IdHTTP1: TIdHTTP;
begin
IdHTTP1.Create;
GetUrlContent:=IdHTTP1.Get(s);
IdHTTP1.Destroy;
end;
procedure DolarUpdate;
var
json: string;
obj: TJSONObject;
url: string;
begin
try
json:=GetUrlContent('http://www.bitven.com/assets/js/rates.js');
try
obj := TJSONObject.ParseJSONValue(json) as TJSONObject;
TabbedForm.Edit2.Text := obj.Values['USD_TO_BSF_RATE'].Value;
finally
obj.Free;
end;
except
on E : Exception do
begin
ShowMessage('Error'+sLineBreak+E.ClassName+sLineBreak +E.Message);
end;
end;
end;
My app doesn't function correctly, nor return any messages. It only crashes.
What am I doing wrong?
Your GetUrlContent() function is not coded correctly. It needs to look like this instead:
function GetUrlContent(s: string): string;
var
IdHTTP1: TIdHTTP;
begin
IdHTTP1 := TIdHTTP.Create;
try
Result := IdHTTP1.Get(s);
finally
IdHTTP1.Free;
end;
end;
And your DolarUpdate() procedure should look more like this instead:
procedure DolarUpdate;
var
json: string;
obj: TJSONObject;
url: string;
begin
try
json := GetUrlContent('http://www.bitven.com/assets/js/rates.js');
obj := TJSONObject.ParseJSONValue(json) as TJSONObject;
if obj = nil then raise Exception.Create('Error parsing JSON');
try
TabbedForm.Edit2.Text := obj.Values['USD_TO_BSF_RATE'].Value;
finally
obj.Free;
end;
except
on E : Exception do
begin
ShowMessage('Error' + sLineBreak + E.ClassName + sLineBreak + E.Message);
end;
end;
end;
I have the following JSON:
{
"Info": {
"User": 2,
"String": "foo"
}
}
Unfortunately TLama's Inno JSON Config library doesn't work with JSON strings but only with json files.
I tried to use JSON string instead of path to json file, but it didn't work.
if JSONQueryInteger('{"Info":{"User":2,"String":"foo"}}', 'Info', 'User', 0, IntValue) then
MsgBox('User=' + IntToStr(IntValue), mbInformation, MB_OK);
I know I could save my JSON to a file and then parse it but it seems kind of messy.
How to parse a JSON string in Inno Setup?
You can use JsonParser library instead. It can parse JSON strings.
It's not as easy to use as JSONConfig.dll – but that's the reason why it is more flexible. Also it's a native Pascal Script code. So, it not only saves you from a temporary .json file, but also from a temporary .dll.
The code can be like:
[Code]
#include "JsonParser.pas"
function GetJsonRoot(Output: TJsonParserOutput): TJsonObject;
begin
Result := Output.Objects[0];
end;
function FindJsonValue(
Output: TJsonParserOutput; Parent: TJsonObject; Key: TJsonString;
var Value: TJsonValue): Boolean;
var
I: Integer;
begin
for I := 0 to Length(Parent) - 1 do
begin
if Parent[I].Key = Key then
begin
Value := Parent[I].Value;
Result := True;
Exit;
end;
end;
Result := False;
end;
function FindJsonObject(
Output: TJsonParserOutput; Parent: TJsonObject; Key: TJsonString;
var Object: TJsonObject): Boolean;
var
JsonValue: TJsonValue;
begin
Result :=
FindJsonValue(Output, Parent, Key, JsonValue) and
(JsonValue.Kind = JVKObject);
if Result then
begin
Object := Output.Objects[JsonValue.Index];
end;
end;
function FindJsonNumber(
Output: TJsonParserOutput; Parent: TJsonObject; Key: TJsonString;
var Number: TJsonNumber): Boolean;
var
JsonValue: TJsonValue;
begin
Result :=
FindJsonValue(Output, Parent, Key, JsonValue) and
(JsonValue.Kind = JVKNumber);
if Result then
begin
Number := Output.Numbers[JsonValue.Index];
end;
end;
function FindJsonString(
Output: TJsonParserOutput; Parent: TJsonObject; Key: TJsonString;
var Str: TJsonString): Boolean;
var
JsonValue: TJsonValue;
begin
Result :=
FindJsonValue(Output, Parent, Key, JsonValue) and
(JsonValue.Kind = JVKString);
if Result then
begin
Str := Output.Strings[JsonValue.Index];
end;
end;
function ParseJsonAndLogErrors(
var JsonParser: TJsonParser; const Source: WideString): Boolean;
var
I: Integer;
begin
ParseJson(JsonParser, Source);
Result := (Length(JsonParser.Output.Errors) = 0);
if not Result then
begin
Log('Error parsing JSON');
for I := 0 to Length(JsonParser.Output.Errors) - 1 do
begin
Log(JsonParser.Output.Errors[I]);
end;
end;
end;
procedure ParseJsonString;
var
Json: string;
JsonParser: TJsonParser;
I: Integer;
JsonRoot, InfoObject: TJsonObject;
UserNumber: TJsonNumber; { = Double }
UserString: TJsonString; { = WideString = string }
begin
Json := '{"Info":{"User":2,"String":"abc"}}';
if ParseJsonAndLogErrors(JsonParser, Json) then
begin
JsonRoot := GetJsonRoot(JsonParser.Output);
if FindJsonObject(JsonParser.Output, JsonRoot, 'Info', InfoObject) and
FindJsonNumber(JsonParser.Output, InfoObject, 'User', UserNumber) and
FindJsonString(JsonParser.Output, InfoObject, 'String', UserString) then
begin
Log(Format('Info:User:%d', [Round(UserNumber)]));
Log(Format('Info:String:%s', [UserString]));
end;
end;
ClearJsonParser(JsonParser);
end;
Another option is to fork the Inno JSON Config library and add support for parsing strings.