unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, IdBaseComponent,
IdComponent, IdTCPConnection, IdTCPClient, IdHTTP, DBXJSON, System.JSON;
type
TForm1 = class(TForm)
Memo1: TMemo;
Button1: TButton;
IdHTTP1: TIdHTTP;
Edit1: TEdit;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
const Url = 'url';
function JSONArrayCovertCnt(usJSON: string): integer;
var
JSONPair : TJSONPair;
JSONArray : TJSONArray;
begin
usJson := StringReplace(usJSON,'\"','"',[rfReplaceAll]);
JSONArray := TJSONObject.ParseJSONValue(usJSON) as TJSONArray;
Result := JSONArray.Count;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
stream: TStringStream; idHttpObj: TIdHTTP;
JSONValue : TJSONvalue;
i : integer;
JSONArray : TJSONArray;
result : integer;
begin
stream := TStringStream.Create('', TEncoding.UTF8); //
idHttpObj := TIdHTTP.Create(nil);
idHttpObj.Get(Url, stream);
idHttpObj.Free;
JSONValue := TJSONObject.ParseJSONValue(stream.DataString);
memo1.Clear;
for i := 0 to 3 do
begin
Memo1.Lines.Add('year: ' + JSONValue.GetValue<string>('data['+i.ToString+'].year') + 'year');
Memo1.Lines.Add('hshld: ' + JSONValue.GetValue<string>('data['+i.ToString+'].hshld') + 'hshld');
Memo1.Lines.Add('popltn: ' + JSONValue.GetValue<string>('data['+i.ToString+'].popltn_sm') + '명');
Memo1.Lines.Add('popltn_male: ' + JSONValue.GetValue<string>('data['+i.ToString+'].popltn_male') + '명');
Memo1.Lines.Add('popltn_female: ' + JSONValue.GetValue<string>('data['+i.ToString+'].popltn_female') + '명');
Memo1.Lines.Add('hshld_avrgpopltn: ' + JSONValue.GetValue<string>('data['+i.ToString+'].hshld_avrgpopltn') + '명');
Memo1.Lines.Add('────────────────────────────────────');
end;
Memo1.Lines.Add('resultCode: ' + JSONValue.GetValue<string>('resultCode'));
Memo1.Lines.Add('resultMsg: ' + JSONValue.GetValue<string>('resultMsg'));
Memo1.Lines.Add('numOfRows : ' + JSONValue.GetValue<string>('numOfRows'));
Memo1.Lines.Add('resultCode: ' + JSONValue.GetValue<string>('resultCode'));
Memo1.Lines.Add('totalcount: ' + JSONValue.GetValue<string>('totalCount'));
result := JSONArrayCovertCnt(stream.DataString);
edit1.Text := IntToStr(result);
stream.Free;
end;
end.
{
"data":
[
{
"popltn_female":134301,
"year":"2012",
"hshld_avrgpopltn":2.65,
"hshld":102031,
"popltn_sm":270460,
"popltn_male":136159
}
]
"pageNo":1,
"currentCount":9,
"resultCode":0,
"totalCount":9,
"numOfRows":10,
"resultMsg":"정상"
}
I want to calculate how many data array values are in a JSONValue.
I tried to find, input, and apply the function code through Google search, but I get an error.
Invalid class type cast
How can we solve this problem?
I asked a question using a translator because I am a Korean who is not good at English at all.
The JSON data you have shown represents an object (which is denoted by { }), not an array (which is denoted by [ ]), so ParseJSONValue() will return a TJSONValue pointing at a TJSONObject, not a TJSONArray. As such, the as typecast to TJSONArray will fail with a runtime error. The data field inside the JSON object is an array.
On a side note, there are other problems with your code:
your use of StringReplace() is completely unnecessary and should be removed. Your JSON doesn't contain any '\' characters, but even if it did, removing anything from the JSON is wrong. You need to parse it exactly as it was received.
all of your Create/Free pairs need to be protected with try/finally.
you are leaking both TJSONValue objects that ParseJSONValue() returns.
you don't need the TStringStream at all, as TIdHTTP.Get()has an overload that returns a String.
Related
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.
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.
I am having trouble parsing this JSON data in Delphi 10 Seattle. I want to get the values from the JSON and show them in TLabel components one by one. I am new to JSON and REST, it would be nice if you provide a working example.
{
"countrydata":[
{
"info":{
"ourid":119,
"title":"Pakistan",
"code":"PK",
"source":"https://thevirustracker.com/pakistan-coronavirus-information-pk"
},
"total_cases":5038,
"total_recovered":1026,
"total_unresolved":0,
"total_deaths":86,
"total_new_cases_today":27,
"total_new_deaths_today":0,
"total_active_cases":3926,
"total_serious_cases":37,
"total_danger_rank":33
}
],
"stat":"ok"
}
EDIT
So far, I have this code but it gives access violation error and no output is given. What am I doing wrong.
procedure TForm1.Button2Click(Sender: TObject);
var
jsonRoot: TJSONObject;
tokenRequest: TRESTRequest;
tokenResponse: TRESTResponse;
tokenClient: TRESTClient;
begin
tokenClient := TRESTClient.Create(nil);
tokenRequest := TRESTRequest.Create(nil);
tokenResponse := TRESTResponse.Create(nil);
try
tokenRequest.Client := tokenClient;
tokenRequest.Response := tokenResponse;
tokenClient.BaseURL := 'https://api.thevirustracker.com/free-api?countryTotal=PK';
tokenRequest.Execute;
jsonRoot:= TJSONObject.ParseJSONValue(tokenResponse.JSONText) as TJSONObject;
Memo1.Lines.Add('TotalCases => ' + jsonRoot.GetValue('total_cases').Value);
Memo1.Lines.Add('TotalRecovered=> ' + jsonRoot.GetValue('total_recovered').Value);
Memo1.Lines.Add('TotalDeaths=> ' + jsonRoot.GetValue('total_deaths').Value);
Memo1.Lines.Add('TotoalNewCases=> ' + jsonRoot.GetValue('total_new_cases_today').Value);
finally
tokenResponse.Free;
tokenRequest.Free;
tokenClient.Free;
end;
end;
You are getting an Access Violation because you are using TJSONObject incorrectly.
All of the JSON values you are trying to read are not immediate children of the top level object of the JSON hierarchy, where you are expecting, so GetValue() returns a nil pointer, and then your code crashes when it tries to read the Value property using that nil pointer.
The values you want are several levels deeper in the JSON hierarchy. The top level object contains a child named countrydata, which is an array of objects. The values you want are children of the 1st object in that array.
Try this instead:
procedure TForm1.Button2Click(Sender: TObject);
var
jsonRoot: TJSONValue;
jsonObj: TJSONObject;
jsonArr: TJSONArray;
tokenRequest: TRESTRequest;
tokenResponse: TRESTResponse;
tokenClient: TRESTClient;
begin
tokenClient := TRESTClient.Create(nil);
try
tokenClient.BaseURL := 'https://api.thevirustracker.com/free-api?countryTotal=PK';
tokenRequest := TRESTRequest.Create(tokenClient);
tokenRequest.Client := tokenClient;
tokenResponse := TRESTResponse.Create(tokenClient);
tokenRequest.Response := tokenResponse;
tokenRequest.Execute;
jsonRoot := TJSONObject.ParseJSONValue(tokenResponse.JSONText);
try
jsonObj := jsonRoot as TJSONObject;
jsonArr := jsonObj.GetValue('countrydata') as TJSONArray;
jsonObj := jsonArr.Items[0] as TJSONObject;
Memo1.Lines.Add('TotalCases => ' + jsonObj.GetValue('total_cases').Value);
Memo1.Lines.Add('TotalRecovered=> ' + jsonObj.GetValue('total_recovered').Value);
Memo1.Lines.Add('TotalDeaths=> ' + jsonObj.GetValue('total_deaths').Value);
Memo1.Lines.Add('TotoalNewCases=> ' + jsonObj.GetValue('total_new_cases_today').Value);
finally
jsonRoot.Free;
end;
finally
tokenClient.Free;
end;
end;
Alternatively, you can use the TRESTResponse.RootElement and TRESTResponse.JSONValue properties instead of calling TJSONObject.ParseJSONValue() manually:
procedure TForm1.Button2Click(Sender: TObject);
var
jsonObj: TJSONObject;
jsonArr: TJSONArray;
tokenRequest: TRESTRequest;
tokenResponse: TRESTResponse;
tokenClient: TRESTClient;
begin
tokenClient := TRESTClient.Create(nil);
try
tokenClient.BaseURL := 'https://api.thevirustracker.com/free-api?countryTotal=PK';
tokenRequest := TRESTRequest.Create(tokenClient);
tokenRequest.Client := tokenClient;
tokenResponse := TRESTResponse.Create(tokenClient);
tokenRequest.Response := tokenResponse;
tokenResponse.RootElement := 'countrydata';
tokenRequest.Execute;
jsonArr := tokenResponse.JSONValue as TJSONArray;
jsonObj := jsonArr.Items[0] as TJSONObject;
Memo1.Lines.Add('TotalCases => ' + jsonObj.GetValue('total_cases').Value);
Memo1.Lines.Add('TotalRecovered=> ' + jsonObj.GetValue('total_recovered').Value);
Memo1.Lines.Add('TotalDeaths=> ' + jsonObj.GetValue('total_deaths').Value);
Memo1.Lines.Add('TotoalNewCases=> ' + jsonObj.GetValue('total_new_cases_today').Value);
finally
tokenClient.Free;
end;
end;
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;
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.