Reputation: 444
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.
Upvotes: 3
Views: 1706
Reputation: 6013
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.
Upvotes: 0
Reputation: 1659
First, you probably want your declaration of TDepartment
to 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;
Upvotes: 2