【问题标题】:Create tree structured JSON创建树状结构的 JSON
【发布时间】:2017-06-01 12:34:40
【问题描述】:

我有一个 TDepartment 类型的对象列表,看起来像这样

TDepartment = class
  ID : Integer;
  Name : string;
  ParentDepartmentID : Integer;
end;

我需要创建一个 TJSONObject,其中包含一个部门数组,它们都可以有一个部门数组。所以它的深度是未知的。

我现在处于一个对我来说根本没有意义的地步,但我希望生成的 JSON 看起来像这样:

    "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": []

请记住,每个级别都有未知数量的兄弟姐妹和孩子。 我想我需要编写一个递归过程,但我无法将其可视化。

【问题讨论】:

    标签: json delphi recursion


    【解决方案1】:

    首先,您可能希望TDepartment 的声明与您描述的嵌套结构相匹配:

    TDepartment = class
      ID : Integer;
      Name : string;
      ParentDepartmentID : Integer;
      SubDepartments: array of TDepartment;
    end;
    

    为了序列化,我建议使用 SuperObject 库而不是内置的 JSON 类:

    function TDepartment.Serialize: ISuperObject;
      var Context: TSuperRttiContext;
    begin
      Context := TSuperRttiContext.Create;
      try
        Result := Context.AsJson<TDepartment>(self);
      finally
        Context.Free;
      end;
    end;
    

    在 cmets 中,OP 提到 TDepartment 包含更多字段,但只有问题中的字段应该被序列化;还必须使用TJSONObject,并且一个部门不知道它的孩子。你可以这样做:

    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;
    

    【讨论】:

    • 我简化了代码只是为了做一个例子,实际上 TDepartments 有数百个属性和字段,它是旧框架中上百万行代码库的一部分,所以 JSON 和 TDepartment 确实匹配。这也是我需要使用内置 JSON 类的原因,因为它们会在整个应用程序中使用。
    • 您想将 TDepartment 的所有字段序列化为 JSON,还是只序列化示例中的那些?
    • 只是我的例子中的那些:) 但值得注意的是,父母与其子女之间的唯一关系是“parent_department_id”,而部门本身没有“子部门”属性
    • 那是一种非常糟糕的父子关系建模方法......我不相信你可以轻松地将一个类序列化为 TJSONObject,但如果你只需要几个字段,无论如何,您可能都想自己动手。看看我的答案的编辑。
    • @MattBaech SuperObject 确实更易于使用且经过更好的测试。您可能可以创建它,然后将其导出为 JSON 字符串,然后从该字符串加载您的 TJSONObject。您可以为 TDepartment 注册您的自定义序列化程序,然后使用内置类型转换的 SuperObject 来完成工作
    【解决方案2】:

    我会创建一个平行的树结构,保持原样不变。您当前的结构与您需要的结构相反,因此您可以扫描当前对象并将它们放置在树中。但是在不知道当前结构的情况下很难给出示例代码,但假设所有部门都存在于某种列表中(让我们说称为“部门”)并且“根”部门的父部门 ID 为零,它会有所作为像这样:

    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.
    

    请注意,这仅用于说明目的。你可能不会创建和破坏我拥有的结构。一旦你有了这个结构,你就可以毫无疑问地使用你当前的例程来创建你的 JSON 记录。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-20
      • 2021-12-07
      相关资源
      最近更新 更多