JSON array in Delphi - json

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);

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.

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.

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;

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.

SuperObject - Extract All

How to get ALL 'id' member values from a generic JSON. Without knowing structure of it. Because its very complex and it has a lot of sub objects. It has to loop through all the sub objects.
Again for people that keep on asking where is the example JSON. My question is about how to extract a member value in my case "id" from any generic JSON that has this member inside.
If you don't know the structure of the JSON you receive from somewhere, it is important to note that JSON is "simply" a composite pattern and you can traverse it like any other composite structure. The following example traverse the complete structure in a JSON text and prints the path of any member named 'id'.
procedure ParseJSON;
var
JSONText: string;
JSON: ISuperObject;
begin
// Retrieve JSON as a string into JSONText variable any way you like.
JSON := SO(JSONText);
ProcessObject(JSON.AsObject);
end;
procedure ProcessObject(const aAsObject: TSuperTableString; const aPrefix: string = '');
var
Names: ISuperObject;
Name: string;
Items: ISuperObject;
Item: ISuperObject;
idx: Integer;
Value: string;
ArrayItem: ISuperObject;
begin
if Assigned(aAsObject) then
begin
Names := aAsObject.GetNames;
Items := aAsObject.GetValues;
for idx := 0 to Items.AsArray.Length - 1 do
begin
Name := Names.AsArray[idx].AsString;
Item := Items.AsArray[idx];
if Item.DataType = stObject then
Value := '<Object>'
else if Item.DataType = stArray then
Value := '<Array>'
else
Value := Item.AsString;
if SameText(Name, 'id') then
WriteLn(Format('%s: %s', [aPrefix + Name, Value]));
if Item.DataType = stArray then
for ArrayItem in Item do
ProcessObject(ArrayItem.AsObject, aPrefix + Name + '.');
if Item.DataType = stObject then
ProcessObject(Item.AsObject, aPrefix + Name + '.');
end;
end;
end;