【问题标题】:Problems creating the correct JSON file using superobject or DBXJSON使用超对象或 DBXJSON 创建正确的 JSON 文件时出现问题
【发布时间】:2014-02-20 14:03:56
【问题描述】:

我使用的是 Delphi XE2,我有这个 json 结构要创建:

[
    {
        "Email": "laura@yyyy.com",
        "MobileNumber": "",
        "MobilePrefix": "",
        "Name": "Laura",
        "Fields": [
            {
                "Description": "nominativo",
                "Id": "1",
                "Value": "Laura"
            },
            {
                "Description": "societa",
                "Id": "2",
                "Value": ""
            },
            {
                "Description": "idcontatto",
                "Id": "3",
                "Value": "0"
            }
        ]
    },
    {
        "Email": "paolo@xxxx.com",
        "MobileNumber": "",
        "MobilePrefix": "",
        "Name": "Paolo",
        "Fields": [
            {
                "Description": "nominativo",
                "Id": "1",
                "Value": "Paolo"
            },
            {
                "Description": "societa",
                "Id": "2",
                "Value": ""
            },
            {
                "Description": "idcontatto",
                "Id": "3",
                "Value": "1"
            }
        ]
    }
]

我用超级对象做了几次测试,但还没有得到正确的结果,因为我得到的数组的第一个元素等于第二个。我的困难在于迭代和优化。这是我正在处理的代码:。

json := TSuperObject.Create;    
jsonArray:= TSuperObject.Create(stArray);

json.S['Email'] := 'laura@yyyy.com';    
json.S['MobileNumber'] := '';    
json.S['MobilePrefix'] := '';    
json.S['Name'] := 'Laura';    
json['Fields'] := SA([]);    
json_Fields:=SO;    
json_Fields.S['Description']:='nominativo';    
json_Fields.S['Id']:='1';    
json_Fields.S['Value']:='Laura';    
json.A['Fields'].Add(json_Fields);    
json_Fields:=SO;    
json_Fields.S['Description']:='societa';    
json_Fields.S['Id']:='2';    
json_Fields.S['Value']:='';    
json.A['Fields'].Add(json_Fields);    
//......other fields    
JsonArray.AsArray.Add(json);

json.S['Email'] := 'paolo@xxxx.com';    
json.S['MobileNumber'] := '';    
json.S['MobilePrefix'] := '';    
json.S['Name'] := 'Paolo';    
json['Fields'] := SA([]);    
json_Fields:=SO;    
json_Fields.S['Description']:='nominativo';    
json_Fields.S['Id']:='1';    
json_Fields.S['Value']:='Paolo';    
json.A['Fields'].Add(json_Fields);    
json_Fields:=SO;    
json_Fields.S['Description']:='societa';    
json_Fields.S['Id']:='2';    
json_Fields.S['Value']:='';    
json.A['Fields'].Add(json_Fields);    
//......other fields    
JsonArray.AsArray.Add(json);

jsonArray.SaveTo('json_mu.txt');    

【问题讨论】:

  • 你尝试了什么,哪里没用?在询问有关您编写的代码的问题时,您需要包含相关代码,或者更好的是 SSCCE
  • 我已经编辑了问题并通过jsonlint.com 格式化了 JSON,这将验证它是正确的 JSON。
  • 请出示您的代码

标签: json delphi superobject dbxjson


【解决方案1】:

您可以使用我们的record-based JSON serialization

首先定义一条记录,包含预期的数据项,以及相应的动态数组:

type
  TMyRecord: record
    Name: string;
    Email: string;
    MobileNumber: string;
    MobilePrefix: string;
    Fields: array of record
      Id: integer;
      Description: string;
      Value: string;
    end;
  end;
  TMyRecordDynArray = array of TMyRecord;

然后你注册记录内容:

const // text description of the record layout
  __TMyRecord = 'Name,Email,MobileNumber,MobilePrefix string '+
    'Fields[Id integer Description,Value string]';

  ...

  TTextWriter.RegisterCustomJSONSerializerFromText(
    TypeInfo(TMyRecord),__TMyRecord);

您可以将数据从 / 加载或保存到 JSON:

var values: TMyRecordDynArray;
    i: integer;

  DynArrayLoadJSON(values,pointer(StringToUTF8(text)),TypeInfo(TMyRecordDynArray));
  for i := 0 to high(values) do
    writeln('name: ',values[i].Name,' mobile: ',values[i].MobilePrefix,' fields count:',length(values[i].Fields));
  DynArraySaveJSON(values,TypeInfo(TMyRecordDynArray));

此 JSON 序列化程序将使用比替代方案更少的内存,可能会更快,并且您可以在编译时检查属性名称。

从 Delphi 6 到 XE5 都可以使用。

编辑:

例如,填充数组:

var R: TMyRecordDynArray;

SetLength(R,2);
with R[0] do begin
  Email := 'laura@yyyy.com';
  Name := 'Laura';
  Setlength(Fields,3);
  Fields[0].Description := 'nominativo';
  Fields[0].Id := 1;
  Fields[0].Value := 'Laura';
  Fields[1].Description := 'societa';
  Fields[1].Id := 2;
  Fields[2].Description := 'idcontatto';
  Fields[2].Id := 3;
  Fields[2].Value := '0';
end;
with R[1] do begin
  Email := 'paolo@xxxx.com';
  Name := 'Paolo';
  Setlength(Fields,3);
  Fields[0].Description := 'nominativo';
  Fields[0].Id := 1;
  Fields[0].Value := 'Paolo';
  Fields[1].Description := 'societa';
  Fields[1].Id := 2;
  Fields[2].Description := 'idcontatto';
  Fields[2].Id := 3;
  Fields[2].Value := '1';
end;

json := DynArraySaveJSON(R,TypeInfo(TMyRecordDynArray));

如果您不喜欢with 语句,您可以在任何地方添加R[0].。我怀疑这不是重点。

我希望它能够显示使用编译时结构而不是定义为文本的后期绑定属性的好处(就像您使用 SuperObject 一样):您不会期望在运行时出现任何关于属性名称的问题,或一般逻辑。

如果您编写 json_Fields.S['DescriptioM']:='nominativo',您将不会在 IDE 中看到任何错误,而 Fields[0].DescriptioM := 'nominativo' 将无法编译。

如果您想更改属性名称,可以在 IDE 中对其进行重构,而不会忘记代码中的某个位置 - 它不会编译。

如果您的业务代码使用高级 Delphi 结构,您将需要使用大多数备用库手动编写一些容易出错的代码来将这些高级值转换为 JSON 或从 JSON 转换。而使用这样的解决方案,您可以定义自己的值对象record,甚至在类型定义中添加一些方法,并直接在您的业务代码中使用它,而无需担心 JSON 持久层的实现细节。简而言之:您想在业务代码中添加对 JSON 库的依赖吗?听起来像是违反了我们试图遵循的一些原则when defining Domain-Driven Design code

还要与您在答案中添加的代码进行比较,了解您的代码的可读性和可维护性。

还有一个好处是所有未定义的字符串字段默认用 '' 初始化(与任何动态数组一样)。

编辑2:

作为Jan wrote in his own answer,您可以使用 SuperObject 直接序列化记录和动态数组,就像我们的类一样。因此,如果您想使用 SuperObject,我的建议是使用此功能,并使用 Delphi 高级类型。请注意,SuperObject 序列化可能比我们单元中的序列化要慢,并且不适用于旧版本的 Delphi(而我们的单元可以使用 Delphi 6/7,例如)。

【讨论】:

  • 非常感谢您的建议,但我现在更愿意使用超级对象。
  • @user3319069 我明白了。但是您在最初的请求中(在编辑和添加代码之前)说您尝试了几个 JSON 库,所以我只是提出了一个可行的替代方案。请参阅我对答案的编辑,也许您会发现使用高级 Delphi 结构而不是后期绑定属性的好处。
【解决方案2】:

使用超级对象:

定义要使用的 Delphi 数据结构,而不是手动构建 JSON 结构,然后使用 ToJSON 将 Delphi 对象转换为 JSON 结构:

Uses SuperObject;

type
   FieldRec = record
      ID: Integer;
      Description,
      Value: String;
   end;
   FieldArr = Array of FieldRec;
   BaseRec = record
      Fields: FieldArr;
   end;
   BaseArr = Array of BaseRec;

   OutputObject = class
      OutputData: BaseArr;
   end;

procedure TFrmAnotherJSONExample.FormShow(Sender: TObject);
var
   sObj: ISuperObject;
   lFieldArr: FieldArr;
   lBaseArr : BaseArr;
   lOutputObject: OutputObject;
begin
  SetLength(lBaseArr,2);
  SetLength(lFieldArr,3);
  for i := 0 to 2 do
  begin
     lFieldArr[i].ID := 10*i;
     lFieldArr[i].Description := 'Description' + IntToStr(lFieldArr[0].ID);
     lFieldArr[i].Value := 'Name' + IntToStr(lFieldArr[0].ID);
  end;
  lBaseArr[0].Fields := lFieldArr;
  for i := 0 to 2 do
  begin
     lFieldArr[i].ID := 100*i;
     lFieldArr[i].Description := 'Description' + IntToStr(lFieldArr[0].ID);
     lFieldArr[i].Value := 'Name' + IntToStr(lFieldArr[0].ID);
  end;
  lBaseArr[1].Fields := lFieldArr;
  lOutputObject := OutputObject.Create;
  lOutputObject.OutputData := lBaseArr;
  sObj := lOutputObject.ToJSON;
  lOutputObject.Free;
  Memo1.Lines.Add(sObj.AsJSON(true));
end;

以上输出:

{
 "OutputData": [
  {
   "Fields": [
    {
     "Description": "Description0",
     "ID": 0,
     "Value": "Name0"
    },{
     "Description": "Description0",
     "ID": 100,
     "Value": "Name0"
    },{
     "Description": "Description0",
     "ID": 200,
     "Value": "Name0"
    }]
  },
 {
   "Fields": [
    {
     "Description": "Description0",
     "ID": 0,
     "Value": "Name0"
    },{
     "Description": "Description0",
     "ID": 100,
     "Value": "Name0"
    },{
     "Description": "Description0",
     "ID": 200,
     "Value": "Name0"
    }]
  }]
}

通过这种方式您可以获得命名数组,我认为这很好(我会推荐它)。
此外,它具有定义 JSON 对象的外部 {}。

补充:发布后我发现我忘记填写 EmailMobileNumber 等字段,但现在这很简单,我将其作为练习留给读者;-)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-10-19
    • 2021-07-04
    • 1970-01-01
    • 1970-01-01
    • 2021-04-09
    • 2011-11-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多