TJSON.JsonToObject<classname> not working in Delphi - json

I am trying to convert Json trying to class object, but the values are not appearing in the new object variable. The resulting object has blank value in string and 0 in integer.
Thanks in advance.
Code:
type
Student =class
public
Name : string;
Age : Integer;
end;
procedure TForm2.Button5Click(Sender: TObject);
var
Student1, Student2: Student;
STR: string;
begin
Student1 := Student.Create;
Student2 := Student.Create;
try
Student1.Name := 'Sam';
Student1.Age := 24;
str := TJson.ObjectToJsonString(Student1);
Form2.outputMemo.Lines.Add(str);
Student2 := TJSON.JsonToObject<Student>(str);
Form2.outputMemo.Lines.Add(Student2.Name);
Form2.outputMemo.Lines.Add(Student2.Age.ToString);
finally
Student1.Free;
Student2.Free;
end;
//Form2.outputMemo.Lines.Text :=TJson.ObjectToJsonObject(Student1);
end;
Output:
{"name":"Sam","age":24}
0
Edit:
I just saw this, and it worked when I changed the names to FName and FAge... what a sorcery!, can anyone please explain the logic behind this?
Delphi Rest.JSON JsonToObject only works with f variables

The internal mapping of JSON fields to Delphi fields is prefixing them with F and changing the following character to upper case. If you want complete control over this you can specify the JSON name with an attribute:
type
Student =class
public
[JSONName('name')]
Name : string;
[JSONName('age')]
Age : Integer;
end;
Note that the JSON names given are case sensitive.
You need to include REST.Json.Types to the uses so that the attribute declaration can be found.

Related

How to convert a PL SQL collection into json_object?

I have a type named codelist if table of varchar. a variable called "final" of type codelist needs to be converted as json_object.
Like this New = ('1','2','3') needs to be converted to json format like below
{
"New" : ['1','2','3']
}
Please help
try this
declare
tag1 json_object_t;
tag2 json_object_t;
array json_array_t;
begin
tag1:= json_object_t();
tag1.put('Date', sysdate);
tag1.put('Your header', "hellow");
tag2:= json_object_t();
tag2.put('pos1', test(1).1);
tag2.put('pos2', teste(2).2);
array.append(tag2);
end;
If I wasn't very clear, you can follow this link - https://dzone.com/articles/relational-to-json-with-plsql

Create tree structured JSON

I have a list of objects, of type TDepartment which looks like this
TDepartment = class
ID : Integer;
Name : string;
ParentDepartmentID : Integer;
end;
I need to create a TJSONObject, with an array of departments, which all can also have an array of departments. So the depth of this is unknown.
I am at a point right now where it simply doesn't make sense to me, but I would like the resulting JSON to look like this:
"department_id": "5",
"department_name": "100",
"parent_dept_id": "",
"subdepartments": [{
"department_id": "8",
"department_name": "300",
"parent_dept_id": "5",
"subdepartments": [{
"department_id": "1",
"department_name": "310",
"parent_dept_id": "8",
"subdepartments": []
Keep in mind that each level has unknown amount of siblings and children.
I guess i need to write a recursive procedure, but I am unable to visualize it.
First, you probably want your declaration of TDepartmentto match the nested structure you describe:
TDepartment = class
ID : Integer;
Name : string;
ParentDepartmentID : Integer;
SubDepartments: array of TDepartment;
end;
In order to serialize this I would recommend using the SuperObject library rather than the inbuilt JSON classes:
function TDepartment.Serialize: ISuperObject;
var Context: TSuperRttiContext;
begin
Context := TSuperRttiContext.Create;
try
Result := Context.AsJson<TDepartment>(self);
finally
Context.Free;
end;
end;
In the comments, OP mentioned that TDepartment contains a lot more fields, but only the ones in the question should be serialized; also TJSONObject has to be used, and a department does not know about its children. You could do something like that:
function TDepartment.Serialize2(AllDepartments: TList<TDepartment>): TJSONObject;
var Department: TDepartment;
Subdepartments: TJSONArray;
begin
Result := TJSONObject.Create;
Result.AddPair(TJSONPair.Create('department_id', TJSONNumber.Create(ID)));
Result.AddPair(TJSONPair.Create('department_name', Name));
Result.AddPair(TJSONPair.Create('parent_dept_id', TJSONNumber.Create(ParentDepartmentID)));
Subdepartments := TJSonArray.Create;
for Department in AllDepartments do
begin
if (Department.ParentDepartmentID <> ID) then Continue;
Subdepartments.AddElement(Department.Serialize2(AllDepartments));
end;
Result.AddPair(TJSONPair.Create('subdepartments', Subdepartments));
end;
I would create a parallel tree structure leaving the original intact. Your current structure is inverted as to what you need, so you scan through your current objects placing them in the tree. But without knowing the current structure this is difficult give sample code, but assuming all departments exist in some sort of list, (let us say called 'Departments') and the 'root' department has a parent department ID of zero it would go something like this:
unit Unit1;
interface
uses
System.Generics.Collections;
type
TDepartment = class
ID : Integer;
Name : string;
ParentDepartmentID : Integer;
end;
TDepartmentStructure = class
ID : Integer;
Name : string;
ParentDepartmentID : Integer;
SubDepartments: TList< TDepartmentStructure >;
constructor Create( const pBasedOn : TDepartment );
end;
var
Department : TObjectList<TDepartment>;
function CopyStructure( pDepartment : TList<TDepartment> ) : TDepartmentStructure; // returns root
implementation
var
DepartmentStructure : TObjectList<TDepartmentStructure>;
function CopyStructure( pDepartment : TList<TDepartment> ) : TDepartmentStructure;
var
i, j: Integer;
begin
// stage one - copy everything
for i := 0 to pDepartment.Count - 1 do
begin
DepartmentStructure.Add( TDepartmentStructure.Create( pDepartment[ i ] ));
end;
// now go through and build structure
Result := nil;
for i := 0 to DepartmentStructure.Count - 1 do
begin
if DepartmentStructure[ i ].ID = 0 then
begin
// root
Result := DepartmentStructure[ i ];
end
else
begin
for j := 0 to DepartmentStructure.Count - 1 do
begin
if DepartmentStructure[ i ].ParentDepartmentID = DepartmentStructure[ j ].ID then
begin
DepartmentStructure[ j ].SubDepartments.Add( DepartmentStructure[ i ] );
break;
end;
end;
end;
end;
end;
{ TDepartmentStructure }
constructor TDepartmentStructure.Create(const pBasedOn: TDepartment);
begin
inherited Create;
ID := pBasedOn.ID;
Name := pBasedOn.Name;
ParentDepartmentID := pBasedOn.ParentDepartmentID;
SubDepartments:= TObjectList< TDepartmentStructure >.Create( FALSE ); // we do NOT own these objects!
end;
initialization
DepartmentStructure := TObjectList<TDepartmentStructure>.Create( TRUE );
finalization
DepartmentStructure.Free;
end.
Note that this is for illustration purposes only. You would probably not create and destroy the structures where I have. Once you have the structure you can create your JSON records using your current routines no doubt.

Get key names in json file in delphi

I have a json string format like this:
{
"LIST":{
"Joseph":{
"item1":0,
"item2":0
},
"John":{
"item1":0,
"item2":0
},
"Fred":{
"item1":0,
"item2":0
}
}
}
I need to get the names, "Joseph", "John", "Fred" and so on... I have a function that will add names to the list, I have no idea what names will be added so I need to get those names.
I can only get the name "LIST" with this code:
js := TlkJSONstreamed.loadfromfile(jsonFile) as TlkJsonObject;
try
ShowMessage( vartostr(js.NameOf[0]) );
finally
s.free;
end;
I'm using lkJSON-1.07 in delphi 7
You can get the names in turn and obtain the next object for each name.
Get the name: js.NameOf[0]
Obtain the object from the name: js[js.NameOf[0]]
The getJSONNames procedure prints all the names contained in a TlkJSONobject object recursively.
procedure getJSONNames(const Ajs: TlkJSONobject);
var
i: Integer;
begin
if Ajs = nil then
Exit
else
for i := 0 to Ajs.Count-1 do begin
WriteLn(Ajs.NameOf[i]);
getJSONNames(TlkJSONobject(Ajs[Ajs.NameOf[i]]));
end;
end;
var
js: TlkJsonObject;
begin
js := TlkJSONstreamed.loadfromfile(jsonFile) as TlkJsonObject;
try
getJSONNames(js);
finally
js.free;
end;
end.

Json Array into ListBox/Memo in Delphi xe7

I'm trying to catch the following JSON array :
[{"name":"Bryan","email":"Bryan#hotmail.com"},
{"name":"Louis","email":"Louis#hotmail.com"},
{"name":"Maria","email":"Maria#hotmail.com"},
{"name":"Test","email":"test#hotmail.com"},
{"name":"Anthony","email":"anthony#hotmail.com"}]
and put it in Memo or ListBox in Delphi :
the code is the following :
procedure TForm1.Button1Click(Sender: TObject);
var jv: TJSONValue;
jo: TJSONObject;
jp: TJSONPair;
ja: TJSONArray;
i: integer;
j: integer;
begin
RESTRequest1.Execute;
jv:=RESTResponse1.JSONValue;
jo:= TJSONObject.ParseJSONValue(jv.ToString) as TJSONObject;
try
for i := 0 to jo.Size - 1 do
begin
jp := jo.Get(i);
if jp.JsonValue is TJSONArray then
begin
ja := jp.JsonValue as TJSONArray;
for j := 0 to ja.Size -1 do
Memo1.Lines.Add(ja.Get(i).ClassName + ': ' + ja.Get(j).ToString);
end
else
Memo1.Lines.Add(jp.ClassName + ': '+ jp.ToString);
end;
finally
jo.Free;
end;
end;
When I click in Button I got the following error message :
Invalid class typecast
during debugging the following line has a problem :
jo:= TJSONObject.ParseJSONValue(jv.ToString) as TJSONObject;
I don't know how to resolve this problem or this mistake ,
Could you please help me ?
Thanks.
This could perfectly well be worked out by reading the code and looking at the JSON. However, I'd like to show you how to go about debugging such a problem in case you cannot work it out by static analysis. When an as cast fails that is always because the object on the left hand side of the as does not derive from the type on the right hand side. The next step then is always to inquire as to what the type of the object on the left hand side is. I've included a short MCVE above as a means to demonstrate.
The output of this program:
{$APPTYPE CONSOLE}
uses
System.JSON;
const
JSON = '[{"name":"Bryan","email":"Bryan#hotmail.com"},' +
' {"name":"Louis","email":"Louis#hotmail.com"},' +
' {"name":"Maria","email":"Maria#hotmail.com"},' +
' {"name":"Test","email":"test#hotmail.com"},' +
' {"name":"Anthony","email":"anthony#hotmail.com"}]';
begin
Writeln(TJSONObject.ParseJSONValue(JSON).ClassName);
end.
is
TJSONArray
Now, TJSONArray does not derive from TJSONObject. Hence your as cast raise a runtime error. If you cast the value returned by ParseJSONValue to TJSONArray that will succeed.
This is to be expected since the root of your JSON is an array and is not an object.
You need to modify your code so that it does not assume that the root level is always an object. You need different behaviour for arrays and objects.
I'm not sure what the problem of TJSONObject is with the string you posted.
For some reason it will parse it if you changed it.
{"Persons":[{"name":"Bryan","email":"Bryan#hotmail.com"},{"name":"Louis","email":"Louis#hotmail.com"},{"name":"Maria","email":"Maria#hotmail.com"},{"name":"Test","email":"test#hotmail.com"},{"name":"Anthony","email":"anthony#hotmail.com"}]}
If I run the code as it is I get the following result
If you don't mind using something different than default Delphi units I would suggest superobject (Link here)
superobject will parse your JSON edited and as posted.
Your code would look like this:
Const
MyJSON = '[{"name":"Bryan","email":"Bryan#hotmail.com"},{"name":"Louis","email":"Louis#hotmail.com"},{"name":"Maria","email":"Maria#hotmail.com"},{"name":"Test","email":"test#hotmail.com"},{"name":"Anthony","email":"anthony#hotmail.com"}]';
procedure ParseJSON;
var
obj: ISuperObject;
Ar: TSuperArray;
I: integer;
begin
obj := SO(MyJSON);
if obj.IsType(stArray) then
begin
Ar := obj.AsArray;
try
for I := 0 to Ar.Length-1 do
L.Add(Ar.O[I].AsString);
finally
Ar.Free;
end;
end
else
L.Add(Obj.AsString);
end;
Result:
For Koul, to get the element names and values.
Like I said not very pretty code but ok.
Ar.O[0].AsObject.GetNames.AsArray.S[0]
To cut it up in pieces a bit.
Ar.O[0] //Get the first element in the array as ISuperObject
.AsObject //Get it as TSuperTableString
.GetNames //Gets all names in the array, in this case "name" and "email"
.AsArray[0]//Get the first name in the names array.
It will result in email (Names are sorted A-Z)
You can do the same for the values by calling GetValues instead of GetNames.
I think the prettiest way to get it will be defining 2x more TSuperArray
procedure PrintNamesAndValues;
Var
Ar, ArNames, ArValues:TSuperArray;
I: Integer;
begin
Ar := SO(<JSON string>).asArray;
ArNames := Ar.O[0].AsObject.GetNames.AsArray;
ArValues := Ar.O[0].AsObject.GetValues.AsArray;
For I := 0 to ArNames.Length-1 do
WriteLn(Format('%s: %s',[ArNames.S[I], ArValues.S[I]]));
end;
Hope it's all clear enough :)

TJson.JsonToObject<T> throws errors in a multi-thread environment

When using TJson.JsonToObject in a multi-thread environment random access violations occur. I was searching a long time for the problem and I could isolate it with the following code
JSON class
type
TParameter = class
public
FName : string;
FDataType : string;
FValue : string;
end;
Testfunction:
procedure Test();
var
myTasks: array of ITask;
i : integer;
max : integer;
begin
max := 50;
SetLength(myTasks, max);
for i := 0 to max -1 do begin
myTasks[i] := TTask.Create(procedure ()
var
json : string;
p : TParameter;
begin
json := '{"name":"NameOfParam","dataType":"TypeOfParam","value":"ValueOfParam"}';
p := TJson.JsonToObject<TParameter>(json);
p.Free;
end);
myTasks[i].Start;
end;
TTask.WaitForAll(myTasks);
ShowMessage('all done!');
end;
It's only a code snippet based of a much more complex source. As long I use this code in a single thread everything works without a problem. I'm wondering if there is anything wrong with the code.
The method TJSONUnMarshal.ObjectInstance in REST.JsonReflect.pas has a severe bug:
It calls FreeAndNil on a TRttiType instance. This should never be done because all TRtti*** instances are managed by the TRttiContext.
After I removed the FreeAndNil call I could not reproduce the access violation anymore.
Reported as: https://quality.embarcadero.com/browse/RSP-10035
P.S. I also think that https://quality.embarcadero.com/browse/RSP-9815 will affect your code.