Marshal a Record to JSON and back [duplicate] - json

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.

Related

Delphi "Invalid class type case" issue

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.

Retrieving JSON data from URL in Delphi XE7

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 to parse a JSON string in Inno Setup?

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.

JSON array in Delphi

I have to place a JSON string to a HTTP request's body. One of the string value must be a JSON array. This is how I tried:
uses
DBXJSON;
const
cContent = 'hello world';
var
LJSONObject: TJSONObject;
x: TBytes;
i: integer;
Temp: string;
begin
LJSONObject:= TJSONObject.Create;
LJSONObject.AddPair('id1', 'value1');
LJSONObject.AddPair('id2', 'value2');
LJSONObject.AddPair('id2', 'value3');
x:= TEncoding.ANSI.GetBytes(cContent);
Temp:= '';
for i := 0 to Length(x) - 1 do
Temp:= Temp + IntToStr(x[i]) + ',';
Delete(Temp, Length(Temp), 1);
LJSONObject.AddPair('id4', '[' + Temp + ']');
ShowMessage(LJSONObject.ToString);
end;
This one is not working, because the value will be encapsulated in double quotes. What is the proper way to pass an array value to the JSONObject?
You are passing a string rather than an array. Hence the result you observe. As a rule, if you find yourself assembling the JSON manually, you are doing it wrong.
Pass an array:
var
arr: TJSONArray;
b: Byte;
....
arr := TJSONArray.Create;
for b in TEncoding.ANSI.GetBytes(cContent) do
arr.Add(b);
LJSONObject.AddPair('id4', arr);

Store Array of Record in JSON

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.