使用superobject或DBXJSON创建正确的JSON文件时出现问题

时间:2014-02-20 14:03:56

标签: json delphi superobject dbxjson

我正在使用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"
            }
        ]
    }
]

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

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');    

2 个答案:

答案 0 :(得分:2)

您可以使用我们的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的原则。

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

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

<强> EDIT2:

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

答案 1 :(得分:1)

使用SuperObject:

定义要使用的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对象的外部{}。

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