Проблемы с созданием правильного файла JSON с использованием суперобъекта или 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"
            }
        ]
    }
]

Я провел несколько тестов с суперобъектом, но еще не достиг правильного результата, потому что первый элемент массива равен второму. Моя сложность заключается в итерации и оптимизации. Это код, над которым я работаю:

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 ответа

Вы можете использовать нашу сериализацию JSON на основе записей.

Сначала вы определяете запись, содержащую ожидаемый элемент данных и соответствующий динамический массив:

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, используя большинство альтернативных библиотек. В то время как с таким решением вы можете определить свои собственные объекты recordдаже добавьте некоторые методы в определение типа и используйте его непосредственно в своем бизнес-коде, не задумываясь о деталях реализации уровня персистентности JSON. Вкратце: вы хотите добавить зависимость к вашей библиотеке JSON в свой бизнес-код? Звучит как нарушение некоторых принципов, которым мы пытаемся следовать при определении кода разработки, управляемого доменом.

Также сравните с кодом, который вы добавили в своем ответе, на предмет читабельности и удобства обслуживания вашего кода.

А также преимущество того, что все неопределенные строковые поля инициализируются с помощью '' по умолчанию (как с любым динамическим массивом).

Edit2:

Как написал Ян в своем собственном ответе, вы можете использовать SuperObject для прямой сериализации записей и динамических массивов, как в случае с нашими классами. Поэтому, если вы не хотите использовать SuperObject, я советую использовать эту функцию и работать с высокоуровневыми типами Delphi. Обратите внимание, что сериализация SuperObject, вероятно, будет медленнее, чем в нашем модуле, и не будет работать со старыми версиями Delphi (тогда как наш модуль работает, например, с Delphi 6/7).

Использование 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.

Добавлено: сразу после публикации я вижу, что я забыл заполнить поля Email, MobileNumber и т. Д., Но это теперь тривиально, и я оставляю это в качестве упражнения для читателя;-)

Другие вопросы по тегам