Delphi pointer variable required - json

I am trying to create a global procedure which can append a JSON field to a main JSON object dynamically.
procedure TJSONAdapter.addField(Key, Field: String);
var
i: Integer;
strArr: TStringList;
j: Integer;
pJSONObj: ^TJSONObject;
begin
strArr:= TStringList.Create;
Split('.', Key, strArr);
pJSONObj:= #STATICJSON;
i:= 0;
repeat
begin
if IsSimpleJsonValue(STATICJSON.GetValue(strArr[i])) then
begin
Exit;
end;
if STATICJSON.GetValue(strArr[i]) is TJSONObject then
begin
pJSONObj:= #(STATICJSON.GetValue(strArr[i]) as TJSONObject); -> error in this line
if i + 1 = strArr.Count then
begin
I store my values in a JSON object named STATICJSON until the program closes, and then I save it to file.
I am trying to add a field somewhere in STATICJSON. For example;
{
"colors" :
{
"id": "F00",
"color": "red",
"details" : [
{
"name_en": "Red",
},
{
"name_de": "Rot"
}
]
},
{
"id": "0F0",
"color": "green",
"details" : [
{
"name_en": "Green",
},
{
"name_de": "Grün"
}
]
}
}
If I want to add a code field in every details of the first object in the colors array:
obj.addField('colors.0.details.X', 'code');
What I expected :
{
"colors" :
{
"id": "F00",
"color": "red",
"details" : [
{
"name_en": "Red",
"code": ""
},
{
"name_de": "Rot",
"code": ""
}
]
},
{
"id": "0F0",
"color": "green",
"details" : [
{
"name_en": "Green",
},
{
"name_de": "Grün"
}
]
}
}
So I am trying to go to the details array of the first color and addField using a pointer.
I was using:
STATICJSON:= STATICJSON.GetValue(strArr[i]) as TJSONObject;
and it works well, but STATICJSON content is changed to this:
"details" : [
{
"name_en": "Red",
"code": ""
},
{
"name_de": "Rot",
"code": ""
}
]
So I lost all other JSON content. This is why I want to use a pointer. But I'm getting an error on this line:
pJSONObj:= #(STATICJSON.GetValue(strArr[i]) as TJSONObject);
Variable required
UPDATE :
procedure TJSONAdapter.addField(Key, Field: String);
var
i: Integer;
strArr: TStringList;
j: Integer;
begin
strArr:= TStringList.Create;
Split('.', Key, strArr);
i:= 0;
repeat
begin
if IsSimpleJsonValue(STATICJSON.GetValue(strArr[i])) then
begin
Exit;
end;
if STATICJSON.GetValue(strArr[i]) is TJSONObject then
begin
STATICJSON:= STATICJSON.GetValue(strArr[i]) as TJSONObject;
if i + 1 = strArr.Count then
begin
STATICJSON.AddPair(TJSONPair.Create(Field, ''));
Exit;
end;
end;
if STATICJSON.GetValue(strArr[i]) is TJSONArray then
begin
if strArr[i + 1] = 'X' then
begin
for j := 0 to (STATICJSON.GetValue(strArr[i]) as TJSONArray).Size -1 do
begin
((STATICJSON.GetValue(strArr[i]) as TJSONArray).Get(j) as TJSONObject).AddPair(TJSONPair.Create(Field, ''));
end;
Exit;
end;
STATICJSON:= (STATICJSON.GetValue(strArr[i]) as TJSONArray).Get(StrToInt(strArr[i+1])) as TJSONObject;
i:= i+1;
end;
Inc(i);
end;
until i = strArr.Count;
strArr.Free;
end;
It works, but because of these lines:
STATICJSON:= STATICJSON.GetValue(strArr[i]) as TJSONObject;
STATICJSON:= (STATICJSON.GetValue(strArr[i]) as TJSONArray).Get(StrToInt(strArr[i+1])) as TJSONObject;
I lost the main JSON content, so I don't want to assign STATICJSON, i just want to assign its address to a pointer to not lose its content.

Delphi objects are reference types. TJSONObject is already a pointer, so there is no need to use ^TJSONObject.
Try something more like this:
var
STATICJSON: TJSONObject = nil;
procedure TJSONAdapter.addField(Key, Field: String);
var
I, J, Index: Integer;
strArr: TStringList;
pJSONVal: TJSONValue;
begin
strArr := TStringList.Create;
try
Split('.', Key, strArr);
if STATICJSON = nil then begin
STATICJSON := TJSONObject.Create;
end;
pJSONVal := STATICJSON;
For I := 0 to strArr.Count-1 then
begin
if TryStrToInt(strArr[I], Index) then
begin
if not (pJSONVal is TJSONArray) then Exit;
pJSONVal := TJSONArray(pJSONVal).Get(Index);
end
else if strArr[I] = '*' then
begin
if I <> (strArr.Count-1) then Exit;
if not (pJSONVal is TJSONArray) then Exit;
with TJSONArray(pJSONVal) do
begin
For J := 0 to Count-1 do
begin
pJSONVal := Get(Index);
if pJSONVal is TJSONObject then
TJSONObject(pJSONVal).AddPair(Field, '');
end;
end;
end
else if pJSONVal is TJSONObject then
begin
pJSONVal := TJSONObject(pJSONVal).Get(strArr[I]);
if pJSONVal = nil then Exit;
end
else Exit;
end;
if pJSONVal is TJSONObject then
TJSONObject(pJSONVal).AddPair(Field, '');
finally
strArr.Free;
end;
end;
obj.addField('colors.0.details.*', 'code');

Related

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;

Inno Setup: Change value in JSON array

I would like to create an installer with the help of Inno Setup. In order to make the program work on every computer, I need to change the tool directory in a .json-file. Here is an excerpt from this file:
{
"commandScriptLinux" : "",
"copyToolBehavior" : "once",
"deleteWorkingDirectoriesAfterWorkflowExecution" : true,
"deleteWorkingDirectoriesKeepOnErrorOnce" : true,
"deleteWorkingDirectoriesNever" : true,
"documentationFilePath" : "",
"enableCommandScriptWindows" : true,
"imitationScript" : "",
"imitationToolOutputFilename" : "",
"launchSettings" :
[
{
"limitInstallationInstancesNumber" : "1",
"limitInstallationInstances" : "false",
"toolDirectory" : "%Selected Setup Folder%",
"version" : "1.0"
}
],
}
I hoped to solve this by using the inno-json-config library. Unfortunately, after executing the code, the lines are reversed (last line comes first now) and the changes weren't made.
[Setup]
AppName=Change_Config
AppVersion=1.0
DefaultDirName={userdocs}\Change_Config
[Files]
Source: "JSONConfig.dll"; Flags: dontcopy
[Code]
function JSONQueryString(FileName, Section, Key, Default: WideString;
var Value: WideString; var ValueLength: Integer): Boolean;
external 'JSONQueryString#files:jsonconfig.dll stdcall';
function JSONWriteString(FileName, Section, Key,
Value: WideString): Boolean;
external 'JSONWriteString#files:jsonconfig.dll stdcall';
procedure InitializeWizard;
var
FileName: WideString;
IntValue: Int64;
StrValue: WideString;
StrLength: Integer;
BoolValue: Boolean;
begin
FileName := 'c:\configuration.json';
SetLength(StrValue, 16);
StrLength := Length(StrValue);
if JSONQueryString(
FileName, 'launchSettings', 'toolDirectory', 'Default', StrValue, StrLength) then
MsgBox('Section_1:Key_1=' + StrValue, mbInformation, MB_OK);
if not JSONWriteString(FileName, 'launchSettings', 'toolDirectory', 'Test') then
MsgBox('JSONWriteString Section_1:Key_1 failed!', mbError, MB_OK);
end;
Thank you very much for your support!
Regards,
Alex
The launchSettings is an array. I believe that the inno-json-config library does not support arrays.
You can use JsonParser library instead.
[Code]
#include "JsonParser.pas"
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 FindJsonArray(
Output: TJsonParserOutput; Parent: TJsonObject; Key: TJsonString;
var Arr: TJsonArray): Boolean;
var
JsonValue: TJsonValue;
begin
Result :=
FindJsonValue(Output, Parent, Key, JsonValue) and
(JsonValue.Kind = JVKArray);
if Result then
begin
Arr := Output.Arrays[JsonValue.Index];
end;
end;
{ ... }
var
JsonLines: TStringList;
JsonParser: TJsonParser;
LaunchSettingsArray: TJsonArray;
ToolDirectoryValue: TJsonValue;
I: Integer;
begin
{ ... }
JsonLines := TStringList.Create;
JsonLines.LoadFromFile(FileName);
ParseJson(JsonParser, JsonLines.Text);
if Length(JsonParser.Output.Errors) > 0 then
begin
Log('Error parsing JSON');
for I := 0 to Length(JsonParser.Output.Errors) - 1 do
begin
Log(JsonParser.Output.Errors[I]);
end;
end
else
begin
if FindJsonArray(
JsonParser.Output, JsonParser.Output.Objects[0],
'launchSettings', LaunchSettingsArray) and
(GetArrayLength(LaunchSettingsArray) >= 0) and
(LaunchSettingsArray[0].Kind = JVKObject) and
FindJsonValue(
JsonParser.Output,
JsonParser.Output.Objects[LaunchSettingsArray[0].Index], 'toolDirectory',
ToolDirectoryValue) and
(ToolDirectoryValue.Kind = JVKString) then
begin
Log(Format(
'launchSettings[0]:toolDirectory:%s', [
JsonParser.Output.Strings[ToolDirectoryValue.Index]]));
JsonParser.Output.Strings[ToolDirectoryValue.Index] := 'Test';
JsonLines.Clear;
PrintJsonParserOutput(JsonParser.Output, JsonLines);
JsonLines.SaveToFile(FileName);
end;
end;
ClearJsonParser(JsonParser);
JsonLines.Free;
end;
It still won't preserve the order (but that should not matter).
Though in your case, you do not need to parse the JSON. You can simply replace the %Selected Setup Folder% with the desired value.
See Replace placeholder in an installed text file with input entered by user.

Delphi parsing a Json with multiple array types?

Following is my JSON:
{
"forms": {
"frmLogin": [
{
"frmLoginPg": "Se connecter - Application de gestion de PC"
},
{
"lbl_login_Title": "Application de gestion Pc"
},
{
"lbl_loginName": "Nom d'utilisateur"
},
{
"lblLanguage": "langue préférée"
},
{
"btnLogin": "Se connecter"
},
{
"btnReset_Loginfrm": "Réinitialiser"
}
],
"frmHome": [
{
"frmHomepg": "Accueil"
},
{
"lbladdUser_Title": "Ajouter un utilisateur"
},
{
"lblName": "prénom"
},
{
"lblEmail": "EMail"
},
{
"popmemFile": "Fichier"
}
]
}
}
I am trying to get the values assigned to each key so I can change the Caption of each component.
I tried the following way, but I am getting an Invalid class typecast error:
function Translationspg.GetTranslationsJson(formNameJson, frmName_FORMJson
: TComponentName; formsam: TForm): string;
var
lJsonBytes: TBytes;
lJsonVal, lJsonScenar: TJSONValue;
lJsonScenarioValue: string; // lJsonString,
lJsonObj: TJSONObject; // , lJsonScenario
lJsonArray: TJSONArray;
lJsonScenarioEntry: TJSOnString;
lJsonPair: TJSONPair;
begin
lJsonBytes := TFile.ReadAllBytes(scJSONFileName_French);
lJsonScenar := TJSONObject.ParseJSONValue(lJsonBytes, 0);
if lJsonScenar <> nil then
begin
lJsonArray := lJsonScenar as TJSONArray;
for lJsonVal in lJsonArray do
begin
lJsonObj := lJsonVal as TJSONObject;
lJsonPair := lJsonObj.Get(formNameJson);
lJsonScenarioEntry := lJsonPair.JsonString;
lJsonScenarioValue := lJsonScenarioEntry.Value;
end;
end;
Result := lJsonScenarioValue;
end;
lJsonArray := lJsonScenar as TJSONArray
The root of your JSON is not an array. It is an object. That objects has a single name/value pair, named forms. You need to read that, and then look for the form by name. Like this:
lJsonObj := TJSONObject.ParseJSONValue(lJsonBytes, 0) as TJSONObject;
lJsonObj := lJsonObj.GetValue('forms') as TJSONObject;
lJsonPair := lJsonObj.Get(formNameJson);
....
This program
{$APPTYPE CONSOLE}
uses
System.SysUtils, System.JSON, System.IOUtils;
procedure Main(const fileName, formName: string);
var
lJsonBytes: TBytes;
lJsonObj: TJSONObject;
lJsonArray: TJSONArray;
lJsonValue: TJSONValue;
lJsonPair: TJSONPair;
begin
lJsonBytes := TFile.ReadAllBytes(fileName);
lJsonObj := TJSONObject.ParseJSONValue(lJsonBytes, 0) as TJSONObject;
lJsonObj := lJsonObj.GetValue('forms') as TJSONObject;
lJsonArray := lJsonObj.GetValue(formName) as TJSONArray;
Writeln(fileName, ' ', formName);
for lJsonValue in lJsonArray do begin
lJsonObj := lJsonValue as TJSONObject;
for lJsonPair in lJsonObj do begin
Writeln(lJsonPair.JsonString.ToString, ': ', lJsonPair.JsonValue.ToString);
end;
end;
Writeln;
end;
begin
try
Main('C:\desktop\json.txt', 'frmLogin');
Main('C:\desktop\json.txt', 'frmHome');
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.
has this output:
C:\desktop\json.txt frmLogin
"frmLoginPg": "Se connecter - Application de gestion de PC"
"lbl_login_Title": "Application de gestion Pc"
"lbl_loginName": "Nom d'utilisateur"
"lblLanguage": "langue préférée"
"btnLogin": "Se connecter"
"btnReset_Loginfrm": "Réinitialiser"
C:\desktop\json.txt frmHome
"frmHomepg": "Accueil"
"lbladdUser_Title": "Ajouter un utilisateur"
"lblName": "prénom"
"lblEmail": "EMail"
"popmemFile": "Fichier"

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"
}
}

Build JSON with Delphi

I'm trying to implement a function which returns a json object containing elements of class. Here is my function:
procedure ListToJson(AInputList: TList<TRating>;
AResponse: TJSONObject);
var
i: Integer;
jsonPair: TJSONPair;
jsonObject: TJSONObject;
jsonArray: TJSONArray;
begin
jsonArray := TJSONArray.Create();
jsonObject := TJSONObject.Create();
jsonPair := TJSONPair.Create('ratings', jsonArray);
for i := 0 to AInputList.Count - 1 do
begin
jsonObject.AddPair(TJSONPair.Create('idrating', IntToStr(AInputList[i].IdRating)));
jsonObject.AddPair(TJSONPair.Create('idmark', IntToStr(AInputList[i].IdMark)));
jsonObject.AddPair(TJSONPair.Create('value', IntToStr(AInputList[i].Value)));
jsonObject.AddPair(TJSONPair.Create('description', AInputList[i].Description));
jsonObject.AddPair(TJSONPair.Create('timeposted', FormatDateTime('yyyy-mm-dd hh:mm:ss', AInputList[i].TimePosted)));
jsonArray.AddElement(jsonObject);
end;
AResponse.AddPair(jsonPair);
end;
And when I test it with a list which contains two elements the returned string is:
{
"ratings":[{
"idrating":"1",
"idmark":"0",
"value":"0",
"description":"",
"timeposted":"2015-07-29 11:25:03",
"idrating":"2",
"idmark":"0",
"value":"0",
"description":"",
"timeposted":"2015-07-29 11:25:24"
},{
"idrating":"1",
"idmark":"0",
"value":"0",
"description":"",
"timeposted":"2015-07-29 11:25:03",
"idrating":"2",
"idmark":"0",
"value":"0",
"description":"",
"timeposted":"2015-07-29 11:25:24"
}]
}
I tried to remove all pairs after each loop iteration:
procedure ListToJson(AInputList: TList<TRating>;
AResponse: TJSONObject);
var
i: Integer;
jsonPair: TJSONPair;
jsonObject: TJSONObject;
jsonArray: TJSONArray;
begin
jsonArray := TJSONArray.Create();
jsonObject := TJSONObject.Create();
jsonPair := TJSONPair.Create('ratings', jsonArray);
for i := 0 to AInputList.Count - 1 do
begin
jsonObject.AddPair(TJSONPair.Create('idrating', IntToStr(AInputList[i].IdRating)));
jsonObject.AddPair(TJSONPair.Create('idmark', IntToStr(AInputList[i].IdMark)));
jsonObject.AddPair(TJSONPair.Create('value', IntToStr(AInputList[i].Value)));
jsonObject.AddPair(TJSONPair.Create('description', AInputList[i].Description));
jsonObject.AddPair(TJSONPair.Create('timeposted', FormatDateTime('yyyy-mm-dd hh:mm:ss', AInputList[i].TimePosted)));
jsonArray.AddElement(jsonObject);
jsonObject.RemovePair('idrating');
jsonObject.RemovePair('idmark');
jsonObject.RemovePair('value');
jsonObject.RemovePair('description');
jsonObject.RemovePair('timeposted');
end;
AResponse.AddPair(jsonPair);
end;
And the ouput is an array with n(count of list elements) empty objects: {"ratings":[{},{}]}
And the json I'm trying to build should be like:
{
"ratings":[{
"idrating":"1",
"idmark":"0",
"value":"0",
"description":"",
"timeposted":"2015-07-29 11:25:03"
},{
"idrating":"2",
"idmark":"0",
"value":"0",
"description":"",
"timeposted":"2015-07-29 11:25:24"
}]
}
You have one jsonObject, so whatever state it ends up in is repeated each time you add it to the array.
Construct jsonObject within the loop, and then you will have different objects in your array.
for i := 0 to AInputList.Count - 1 do
begin
jsonObject := TJSONObject.Create();
jsonObject.AddPair(TJSONPair.Create('idrating', IntToStr(AInputList[i].IdRating)));
jsonObject.AddPair(TJSONPair.Create('idmark', IntToStr(AInputList[i].IdMark)));
jsonObject.AddPair(TJSONPair.Create('value', IntToStr(AInputList[i].Value)));
jsonObject.AddPair(TJSONPair.Create('description', AInputList[i].Description));
jsonObject.AddPair(TJSONPair.Create('timeposted', FormatDateTime('yyyy-mm-dd hh:mm:ss', AInputList[i].TimePosted)));
jsonArray.AddElement(jsonObject);
end;
Alternatly, you can define a function to create a TJSONObject from a TRating, and use that in your loop
function TRatingToJSON( Rating: TRating ): TJSONObject;
begin
Result := TJSONObject.Create();
Result.AddPair(TJSONPair.Create('idrating', IntToStr(Rating.IdRating)));
Result.AddPair(TJSONPair.Create('idmark', IntToStr(Rating.IdMark)));
Result.AddPair(TJSONPair.Create('value', IntToStr(Rating.Value)));
Result.AddPair(TJSONPair.Create('description', Rating.Description));
Result.AddPair(TJSONPair.Create('timeposted', FormatDateTime('yyyy-mm-dd hh:mm:ss', Rating.TimePosted)));
end;
procedure ListToJson(AInputList: TList<TRating>;
AResponse: TJSONObject);
var
i: Integer;
jsonArray: TJSONArray;
begin
jsonArray := TJSONArray.Create();
for i := 0 to AInputList.Count - 1 do
begin
jsonArray.AddElement(TRatingToJSON(AInputList[i]));
end;
AResponse.AddPair(TJSONPair.Create('ratings', jsonArray));
end;
Is it me, or is that just really ugly (and codous, or what's the word for a lot of code for what it does). Since I really (really) hate to see long lists of overloads for all kinds of types, and really (really) like the Variant type, I've created jsonDoc, and it would look like this:
var
x:array of OleVariant;
i:integer;
begin
SetLength(x,AInputList.Count);
for i:=0 to AInputListCount-1 do
x[i]:=JSON(
['idrating',AInputList[i].IdRating
,'idmark',AInputList[i].IdMark
,'value',AInputList[i].Value
,'description',AInputList[i].Description
,'timeposted',FormatDateTime('yyyy-mm-dd hh:mm:ss', AInputList[i].TimePosted)//VarFromDateTime?
]);
end;
AResponse:=JSON(['ratings',VarArrayOf(x)]);
Found a workraround:
procedure ListToJson(AInputList: TList<TRating>;
AResponse: TJSONObject);
var
i: Integer;
jsonPair: TJSONPair;
jsonObject: TList<TJSONObject>;
jsonArray: TJSONArray;
begin
jsonArray := TJSONArray.Create();
jsonObject := TList<TJSONObject>.Create;
jsonPair := TJSONPair.Create('ratings', jsonArray);
try
for i := 0 to AInputList.Count - 1 do
begin
jsonObject.Add(TJSONObject.Create());
jsonObject.Last.AddPair(TJSONPair.Create('idrating', IntToStr(AInputList[i].IdRating)));
jsonObject.Last.AddPair(TJSONPair.Create('idmark', IntToStr(AInputList[i].IdMark)));
jsonObject.Last.AddPair(TJSONPair.Create('value', IntToStr(AInputList[i].Value)));
jsonObject.Last.AddPair(TJSONPair.Create('description', AInputList[i].Description));
jsonObject.Last.AddPair(TJSONPair.Create('timeposted', FormatDateTime('yyyy-mm-dd hh:mm:ss', AInputList[i].TimePosted)));
jsonArray.AddElement(jsonObject.Last);
end;
AResponse.AddPair(jsonPair);
finally
jsonObject.Free;
end;
end;