Giter Club home page Giter Club logo

dataset-serialize's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dataset-serialize's Issues

Argument `AOnlyUpdatedRecords` does not export only changes in the DataSet

I tried to fetch data into a dataset using TFDQuery without any modification to the data, the ToJSONArray(true) exported the entire dataset but I expected ToJSONArray(true) to export an empty array because the dataset is clean.

Please guide me what went wrong, thanks.

procedure ExportChanges;
var
  arr: TJSONArray;
begin
  with qMaster do begin
    CachedUpdates := True;
    Open('SELECT first_name, last_name, email FROM customers');
    CommitUpdates;

    arr := ToJSONArray(true);
    try
      mmoJSON.Lines.Add(arr.Format);
    finally
      arr.Free;
    end;
  end;
end;

DataSet.Serialize no Lazarus

Instalei com Boss em um projeto lazarus porém não reconhece a unit na uses

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Horse, Horse.Jhonson, fpjson, SysUtils, UDM, DataSet.Serialize; 

..
foi criado a pasta /modules no projeto com as middlewares baixadas, mas não encontra no projeto

preciso fazer mais alguma configuração no projeto ou no lazarus ?

Exportar tipos de campo GUID de SQLIte para String

Eu tenho uma tabela no SQLite com o seguinte campo do tipo GUID

CREATE TABLE Medicao (
Codigo INTEGER PRIMARY KEY AUTOINCREMENT ,
Indicador GUID NOT NULL REFERENCES Indicadores (ID) ON DELETE CASCADE
ON UPDATE CASCADE,
Valor DOUBLE(11,3) )

Embora seja sabido que Sqlite é um banco de dados não tipado, é possível usufruir da engine Firedac para detectar automaticamente campos "GUID" como UUID durante o método Autocreate dos fields, em um componente FDQuery por exemplo.

O método abaixo retornou o seguinte erro
"Cannot find type for field "indicador""

uses Dataset.Serialize;
function DataModule1.MeuJsonArray: TJsonArray;
begin
Query1.Open('select codigo, indicador from medicao');
MeuJsonArray := Query1.ToJsonArray();
end;

O erro ocorreu na seguinte linha

unit DataSet.Serialize.Export;

function TDataSetSerialize.DataSetToJSONArray(const ADataSet: TDataSet; const IsChild: Boolean): TJSONArray;
....
case LField.DataType of
TFieldType.ftBoolean:
begin
case TDataSetSerializeUtils.BooleanFieldToType(TBooleanField(LField)) of
bfUnknown, bfBoolean:
Result.AddPair(LKey, TDataSetSerializeUtils.BooleanToJSON(LField.AsBoolean));
else
Result.AddPair(LKey, TJSONNumber.Create(LField.AsInteger));
end;
end;
TFieldType.ftInteger, TFieldType.ftSmallint, TFieldType.ftShortint:
Result.AddPair(LKey, TJSONNumber.Create(LField.AsInteger));
TFieldType.ftLongWord, TFieldType.ftAutoInc:
Result.AddPair(LKey, TJSONNumber.Create(LField.AsWideString));
TFieldType.ftLargeint:
Result.AddPair(LKey, TJSONNumber.Create(LField.AsLargeInt));
TFieldType.ftSingle, TFieldType.ftFloat:
Result.AddPair(LKey, TJSONNumber.Create(LField.AsFloat));
TFieldType.ftString, TFieldType.ftWideString, TFieldType.ftMemo, TFieldType.ftWideMemo:
Result.AddPair(LKey, TJSONString.Create(LField.AsWideString));
TFieldType.ftTimeStamp, TFieldType.ftDateTime, TFieldType.ftTime:
Result.AddPair(LKey, TJSONString.Create(DateToISO8601(LField.AsDateTime, TDataSetSerializeConfig.GetInstance.DateInputIsUTC)));
TFieldType.ftDate:
Result.AddPair(LKey, TJSONString.Create(FormatDateTime(TDataSetSerializeConfig.GetInstance.Export.FormatDate, LField.AsDateTime)));
TFieldType.ftCurrency:
Result.AddPair(LKey, TJSONString.Create(FormatCurr(TDataSetSerializeConfig.GetInstance.Export.FormatCurrency, LField.AsCurrency)));
TFieldType.ftFMTBcd, TFieldType.ftBCD:
Result.AddPair(LKey, TJSONNumber.Create(BcdToDouble(LField.AsBcd)));
TFieldType.ftDataSet:
begin
LNestedDataSet := TDataSetField(LField).NestedDataSet;
Result.AddPair(LKey, DataSetToJSONArray(LNestedDataSet));
end;
TFieldType.ftGraphic, TFieldType.ftBlob, TFieldType.ftOraBlob, TFieldType.ftStream:
Result.AddPair(LKey, TJSONString.Create(EncodingBlobField(LField)));
else
raise EDataSetSerializeException.CreateFmt(FIELD_TYPE_NOT_FOUND, [LKey]);
end;

incluir também
case LField.DataType of
...
TFieldType.ftGuid: Result.AddPair(LKey, TJSONString.Create(LField.AsWideString));

Dynamic structure

Today the load and save structure has only a few static properties. If I control one propertie that is not in this process, I need to add manually.

The save and load structure should handler all the field properties dynamic, maybe with a RTTI help.

Samples\Lazarus\basic\basic.lpi can not compile, TArray<string> not availble yet in FPC?

Hi,

When I try to compile the project basic.lpi I get errors.

unit DataSet.Serialize.Samples.Basic;
(line 47)  FDataSetPrefix: TArray<string>;

From what I've seen on Lazarus Forums

TArray<string> may not yet be implemented in FPC?

Is there a work around for Lazarus yet available please?

unit DataSet.Serialize.Samples.Basic;

Compile Project, Target: basic.exe: Exit code 1, Errors: 3
DataSet.Serialize.Config.pas(47,21) Error: Identifier not found "TArray"
DataSet.Serialize.Config.pas(47,27) Error: Error in type definition
DataSet.Serialize.Config.pas(47,27) Fatal: Syntax error, ";" expected but "<" found

Lazarus 2.0.8 r62944 FPC 3.0.4 x86_64-win64-win32/win64

Kind regards,
Paul

Problema ao utilizar DataSet-Serialize nos Delphi antigos

Eu estava tendo problema ao utilizar DataSet-Serialize no Delphi 7, implementei a biblioteca com boss normalmente.

Pra tira informações do DBGrid e jogar num TEdit ou TMemo por exemplo ele joga normalmente manipulação direto sem problema. mais quando eu quero joga pro DBGrid na tabela e ele não joga. usei variais conexões tabela temporária e n deu, e usei o mesmo código no delphi versão mais recente ele funciona normalmente.

é algum recurso ou configuração eu devo alterar pra q funcione no delphi 7 ??

Problema tratamento fieldtype datetime

Existe um problema em dataset.ToJSONObject e dataset.ToJSONArray para fieldtype datetime.
Result esperado: {"codigo":2552,"dataentrada":"2020-07-10 12:00:00"}
Result produzido: {"codigo":2552,"dataentrada":"10/07/2020 12:00:00"}
Parece que também não está fazendo o tratamento UTC.

Nome dos campos

Bom dia, não vi na documentação com parametro passo para mandar os nomes dos campos como estão na tabela.

ex:
"estados_id" muda para "estadosId"

Grato

Um campo apenas para serializar

Ao serializar apenas de um campo o retorno é o valor do campo apenas, modifiquei para trazer o registro serializado se for informado o argumento AValueRecords = false.

Modificado as seguintes units

DataSet.Serialize.Export

unit DataSet.Serialize.Export;

{$IF DEFINED(FPC)}
  {$MODE DELPHI}{$H+}
{$ENDIF}

interface

uses
{$IF DEFINED(FPC)}
  DB, fpjson;
{$ELSE}
  Data.DB, System.JSON;
{$ENDIF}

type
  TDataSetSerialize = class
  private
    FDataSet: TDataSet;
    FOnlyUpdatedRecords: Boolean;
    FChildRecord: Boolean;
    FValueRecord: Boolean;
    /// <summary>
    ///   Creates a JSON object with the data from the current record of DataSet.
    /// </summary>
    /// <param name="ADataSet">
    ///   Refers to the DataSet that you want to export the record.
    /// </param>
    /// <returns>
    ///   Returns a JSON object containing the record data.
    /// </returns>
    /// <remarks>
    ///   Invisible or null fields will not be exported.
    /// </remarks>
    function DataSetToJSONObject(const ADataSet: TDataSet; const AValue: Boolean = true): TJSONObject;
    /// <summary>
    ///   Creates an array of JSON objects with all DataSet records.
    /// </summary>
    /// <param name="ADataSet">
    ///   Refers to the DataSet that you want to export the records.
    /// </param>
    /// <returns>
    ///   Returns a JSONArray with all records from the DataSet.
    /// </returns>
    /// <remarks>
    ///   Invisible or null fields will not be exported.
    /// </remarks>
    function DataSetToJSONArray(const ADataSet: TDataSet; const IsChild: Boolean; const isValue: Boolean = true ): TJSONArray;
    /// <summary>
    ///   Encrypts a blob field in Base64.
    /// </summary>
    /// <param name="AField">
    ///   Refers to the field of type Blob or similar.
    /// </param>
    /// <returns>
    ///   Returns a string with the cryptogrammed content in Base64.
    /// </returns>
    function EncodingBlobField(const AField: TField): string;
    {$IF NOT DEFINED(FPC)}
    /// <summary>
    ///   Verifiy if a DataSet has detail dataset and if has child modification.
    /// </summary>
    function HasChildModification(const ADataSet: TDataSet): Boolean;
    {$ENDIF}
  public
    /// <summary>
    ///   Responsible for creating a new instance of TDataSetSerialize class.
    /// </summary>
    constructor Create(const ADataSet: TDataSet; const AOnlyUpdatedRecords: Boolean = False; const AChildRecords: Boolean = True; const AValueRecords: Boolean = true);
    /// <summary>
    ///   Creates an array of JSON objects with all DataSet records.
    /// </summary>
    /// <returns>
    ///   Returns a JSONArray with all records from the DataSet.
    /// </returns>
    /// <remarks>
    ///   Invisible or null fields will not be generated.
    /// </remarks>
    function ToJSONArray: TJSONArray;
    /// <summary>
    ///   Creates a JSON object with the data from the current record of DataSet.
    /// </summary>
    /// <returns>
    ///   Returns a JSON object containing the record data.
    /// </returns>
    /// <remarks>
    ///   Invisible or null fields will not be generated.
    /// </remarks>
    function ToJSONObject: TJSONObject;
    /// <summary>
    ///   Responsible for exporting the structure of a DataSet in JSON Array format.
    /// </summary>
    /// <returns>
    ///   Returns a JSON array with all fields of the DataSet.
    /// </returns>
    /// <remarks>
    ///   Invisible fields will not be generated.
    /// </remarks>
    function SaveStructure: TJSONArray;
  end;

implementation

uses
{$IF DEFINED(FPC)}
  DateUtils, SysUtils, Classes, FmtBCD, TypInfo, base64,
{$ELSE}
  System.DateUtils, Data.FmtBcd, System.SysUtils, System.TypInfo, System.Classes, System.NetEncoding, System.Generics.Collections,
  FireDAC.Comp.DataSet,
{$ENDIF}
  DataSet.Serialize.Utils, DataSet.Serialize.Consts, DataSet.Serialize.UpdatedStatus, DataSet.Serialize.Config;

{ TDataSetSerialize }

function TDataSetSerialize.ToJSONObject: TJSONObject;
begin
  Result := DataSetToJSONObject(FDataSet);
end;

function TDataSetSerialize.DataSetToJSONArray(const ADataSet: TDataSet; const IsChild: Boolean; const isValue: Boolean = true): TJSONArray;
var
  LBookMark: TBookmark;
begin
  Result := TJSONArray.Create;
  if ADataSet.IsEmpty then
    Exit;
  try
    LBookMark := ADataSet.BookMark;
    ADataSet.First;
    while not ADataSet.Eof do
    begin
      {$IF DEFINED(FPC)}
      Result.Add(DataSetToJSONObject(ADataSet));
      {$ELSE}
      if IsChild and FOnlyUpdatedRecords then
        if (ADataSet.UpdateStatus = TUpdateStatus.usUnmodified) and not(HasChildModification(ADataSet)) then
        begin
          ADataSet.Next;
          Continue;
        end;
      if (ADataSet.FieldCount = 1)  and (isValue)  then
      begin
        case ADataSet.Fields[0].DataType of
          TFieldType.ftBoolean:
            Result.Add(ADataSet.Fields[0].AsBoolean);
          TFieldType.ftInteger, TFieldType.ftSmallint, TFieldType.ftShortint:
            Result.Add(ADataSet.Fields[0].AsInteger);
          TFieldType.ftLongWord, TFieldType.ftAutoInc, TFieldType.ftString, TFieldType.ftWideString, TFieldType.ftMemo, TFieldType.ftWideMemo, TFieldType.ftGuid:
            Result.Add(ADataSet.Fields[0].AsWideString);
          TFieldType.ftLargeint:
            Result.Add(ADataSet.Fields[0].AsLargeInt);
          TFieldType.ftSingle, TFieldType.ftFloat:
            Result.Add(ADataSet.Fields[0].AsFloat);
          TFieldType.ftDateTime:
               Result.Add(FormatDateTime(TDataSetSerializeConfig.GetInstance.Export.FormatDateTime, ADataSet.Fields[0].AsDateTime));
           TFieldType.ftTimeStamp:
               Result.Add(DateToISO8601(ADataSet.Fields[0].AsDateTime, TDataSetSerializeConfig.GetInstance.DateInputIsUTC));
           TFieldType.ftTime:
               Result.Add(FormatDateTime(TDataSetSerializeConfig.GetInstance.Export.FormatTime, ADataSet.Fields[0].AsDateTime));
           TFieldType.ftDate:
               Result.Add(FormatDateTime(TDataSetSerializeConfig.GetInstance.Export.FormatDate, ADataSet.Fields[0].AsDateTime));
          TFieldType.ftCurrency:
            begin
              if TDataSetSerializeConfig.GetInstance.Export.FormatCurrency.Trim.IsEmpty then
                Result.Add(ADataSet.Fields[0].AsCurrency)
              else
                Result.Add(FormatCurr(TDataSetSerializeConfig.GetInstance.Export.FormatCurrency, ADataSet.Fields[0].AsCurrency));
            end;
          TFieldType.ftFMTBcd, TFieldType.ftBCD:
            Result.Add(BcdToDouble(ADataSet.Fields[0].AsBcd));
          TFieldType.ftGraphic, TFieldType.ftBlob, TFieldType.ftOraBlob, TFieldType.ftStream:
            Result.Add(EncodingBlobField(ADataSet.Fields[0]));
          else
            raise EDataSetSerializeException.CreateFmt(FIELD_TYPE_NOT_FOUND, [ADataSet.Fields[0].FieldName]);
        end;
      end
      else
        Result.AddElement(DataSetToJSONObject(ADataSet));
      {$ENDIF}
      ADataSet.Next;
    end;
  finally
    if ADataSet.BookmarkValid(LBookMark) then
      ADataSet.GotoBookmark(LBookMark);
    ADataSet.FreeBookmark(LBookMark);
  end;
end;

function TDataSetSerialize.DataSetToJSONObject(const ADataSet: TDataSet; const AValue: Boolean = true): TJSONObject;
var
  LKey: string;
  {$IF NOT DEFINED(FPC)}
  LNestedDataSet: TDataSet;
  LDataSetDetails: TList<TDataSet>;
  {$ENDIF}
  LField: TField;
begin
  Result := TJSONObject.Create;
  if not Assigned(ADataSet) or ADataSet.IsEmpty then
    Exit;
  for LField in ADataSet.Fields do
  begin
    if TDataSetSerializeConfig.GetInstance.Export.ExportOnlyFieldsVisible then
      if not(LField.Visible) then
        Continue;
    LKey := TDataSetSerializeUtils.FormatCaseNameDefinition(LField.FieldName);
    if LField.IsNull then
    begin
      if TDataSetSerializeConfig.GetInstance.Export.ExportNullValues then
        if TDataSetSerializeConfig.GetInstance.Export.ExportNullAsEmptyString then
          Result.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(LKey, '')
        else
          Result.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(LKey, TJSONNull.Create);
      Continue;
    end;
    case LField.DataType of
      TFieldType.ftBoolean:
        Result.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(LKey, TDataSetSerializeUtils.BooleanToJSON(LField.AsBoolean));
      TFieldType.ftInteger, TFieldType.ftSmallint{$IF NOT DEFINED(FPC)}, TFieldType.ftShortint, TFieldType.ftWord, TFieldType.ftByte{$ENDIF}:
        Result.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(LKey, {$IF DEFINED(FPC)}LField.AsInteger{$ELSE}TJSONNumber.Create(LField.AsInteger){$ENDIF});
      {$IF NOT DEFINED(FPC)}TFieldType.ftLongWord, {$ENDIF}TFieldType.ftAutoInc:
        Result.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(LKey, {$IF DEFINED(FPC)}LField.AsWideString{$ELSE}TJSONNumber.Create(LField.AsWideString){$ENDIF});
      TFieldType.ftLargeint:
        Result.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(LKey, {$IF DEFINED(FPC)}LField.AsLargeInt{$ELSE}TJSONNumber.Create(LField.AsLargeInt){$ENDIF});
      {$IF NOT DEFINED(FPC)}TFieldType.ftSingle, TFieldType.ftExtended, {$ENDIF}TFieldType.ftFloat:
        Result.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(LKey, {$IF DEFINED(FPC)}LField.AsFloat{$ELSE}TJSONNumber.Create(LField.AsFloat){$ENDIF});
      TFieldType.ftString, TFieldType.ftWideString, TFieldType.ftMemo, TFieldType.ftWideMemo, TFieldType.ftGuid, TFieldType.ftFixedChar, TFieldType.ftFixedWideChar:
        Result.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(LKey, TJSONString.Create(LField.AsWideString));
      TFieldType.ftDateTime:
           Result.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(LKey, TJSONString.Create(FormatDateTime(TDataSetSerializeConfig.GetInstance.Export.FormatDateTime, LField.AsDateTime)));
       TFieldType.ftTimeStamp:
           Result.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(LKey, TJSONString.Create(DateToISO8601(LField.AsDateTime, TDataSetSerializeConfig.GetInstance.DateInputIsUTC)));
       TFieldType.ftTime:
           Result.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(LKey, TJSONString.Create(FormatDateTime(TDataSetSerializeConfig.GetInstance.Export.FormatTime, LField.AsDateTime)));
       TFieldType.ftDate:
           Result.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(LKey, TJSONString.Create(FormatDateTime(TDataSetSerializeConfig.GetInstance.Export.FormatDate, LField.AsDateTime)));
      TFieldType.ftCurrency:
        begin
          if TDataSetSerializeConfig.GetInstance.Export.FormatCurrency.Trim.IsEmpty then
            Result.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(LKey, {$IF DEFINED(FPC)}LField.AsCurrency{$ELSE}TJSONNumber.Create(LField.AsCurrency){$ENDIF})
          else
            Result.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(LKey, TJSONString.Create(FormatCurr(TDataSetSerializeConfig.GetInstance.Export.FormatCurrency, LField.AsCurrency)));
        end;
      TFieldType.ftFMTBcd, TFieldType.ftBCD:
        Result.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(LKey, {$IF DEFINED(FPC)}BcdToDouble(LField.AsBcd){$ELSE}TJSONNumber.Create(BcdToDouble(LField.AsBcd)){$ENDIF});
      {$IF NOT DEFINED(FPC)}
      TFieldType.ftDataSet:
        begin
          LNestedDataSet := TDataSetField(LField).NestedDataSet;
          Result.AddPair(LKey, DataSetToJSONArray(LNestedDataSet, AValue));
        end;
      {$ENDIF}
      TFieldType.ftGraphic, TFieldType.ftBlob, TFieldType.ftOraBlob{$IF NOT DEFINED(FPC)}, TFieldType.ftStream{$ENDIF}:
        Result.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(LKey, TJSONString.Create(EncodingBlobField(LField)));
      else
        raise EDataSetSerializeException.CreateFmt(FIELD_TYPE_NOT_FOUND, [LKey]);
    end;
  end;
  if (FOnlyUpdatedRecords) and (FDataSet <> ADataSet) then
    Result.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(TDataSetSerializeUtils.FormatCaseNameDefinition('object_state'), TJSONString.Create(ADataSet.UpdateStatus.ToString));
  {$IF NOT DEFINED(FPC)}
  if FChildRecord then
  begin
    LDataSetDetails := TList<TDataSet>.Create;
    try
      ADataSet.GetDetailDataSets(LDataSetDetails);
      for LNestedDataSet in LDataSetDetails do
      begin
        if FOnlyUpdatedRecords then
          TFDDataSet(LNestedDataSet).FilterChanges := [rtInserted, rtModified, rtDeleted, rtUnmodified];
        if TDataSetSerializeConfig.GetInstance.Export.ExportEmptyDataSet or (LNestedDataSet.RecordCount > 0) then
          if TDataSetSerializeConfig.GetInstance.Export.ExportChildDataSetAsJsonObject and (LNestedDataSet.RecordCount = 1) then
            Result.AddPair(TDataSetSerializeUtils.FormatDataSetName(LNestedDataSet.Name), DataSetToJsonObject(LNestedDataSet))
          else
            Result.AddPair(TDataSetSerializeUtils.FormatDataSetName(LNestedDataSet.Name), DataSetToJSONArray(LNestedDataSet, True));
        if FOnlyUpdatedRecords then
          TFDDataSet(LNestedDataSet).FilterChanges := [rtInserted, rtModified, rtUnmodified];
      end;
    finally
      LDataSetDetails.Free;
    end;
  end;
  {$ENDIF}
end;

function TDataSetSerialize.EncodingBlobField(const AField: TField): string;
var
  LMemoryStream: TMemoryStream;
  LStringStream: TStringStream;
  {$IF NOT DEFINED(FPC)}
  LBase64Encoding: TBase64Encoding;
  {$ENDIF}
begin
  LMemoryStream := TMemoryStream.Create;
  LStringStream := TStringStream.Create;
  try
    TBlobField(AField).SaveToStream(LMemoryStream);
    LMemoryStream.Position := 0;
    {$IF DEFINED(FPC)}
    LStringStream.LoadFromStream(LMemoryStream);
    Result := EncodeStringBase64(LStringStream.DataString);
    {$ELSE}
    LBase64Encoding := TBase64Encoding.Create(0);
    try
      LBase64Encoding.Encode(LMemoryStream, LStringStream);
    finally
      LBase64Encoding.Free;
    end;
    Result := LStringStream.DataString;
    {$ENDIF}
  finally
    LStringStream.Free;
    LMemoryStream.Free;
  end;
end;

{$IF NOT DEFINED(FPC)}
function TDataSetSerialize.HasChildModification(const ADataSet: TDataSet): Boolean;
var
  LMasterSource: TDataSource;
  LDataSetDetails: TList<TDataSet>;
  LNestedDataSet: TDataSet;
begin
  Result := False;
  LDataSetDetails := TList<TDataSet>.Create;
  try
    ADataSet.GetDetailDataSets(LDataSetDetails);
    for LNestedDataSet in LDataSetDetails do
    begin
      Result := HasChildModification(LNestedDataSet);
      if Result then
        Break;
      if not (LNestedDataSet is TFDDataSet) then
        Continue;
      LMasterSource := TFDDataSet(LNestedDataSet).MasterSource;
      try
        TFDDataSet(LNestedDataSet).MasterSource := nil;
        TFDDataSet(LNestedDataSet).FilterChanges := [rtInserted, rtModified, rtDeleted];
        Result := TFDDataSet(LNestedDataSet).RecordCount > 0;
        if Result then
          Break;
      finally
        TFDDataSet(LNestedDataSet).FilterChanges := [rtInserted, rtModified, rtUnmodified];
        TFDDataSet(LNestedDataSet).MasterSource := LMasterSource;
      end;
    end;
  finally
    LDataSetDetails.Free;
  end;
end;
{$ENDIF}

function TDataSetSerialize.SaveStructure: TJSONArray;
var
  LField: TField;
  LJSONObject: TJSONObject;
begin
  Result := TJSONArray.Create;
  if FDataSet.FieldCount <= 0 then
    Exit;
  for LField in FDataSet.Fields do
  begin
    LJSONObject := TJSONObject.Create;
    LJSONObject.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(FIELD_PROPERTY_ALIGNMENT, TJSONString.Create(GetEnumName(TypeInfo(TAlignment), Ord(LField.Alignment))));
    LJSONObject.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(FIELD_PROPERTY_FIELD_NAME, TJSONString.Create(LField.FieldName));
    LJSONObject.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(FIELD_PROPERTY_DISPLAY_LABEL, TJSONString.Create(LField.DisplayLabel));
    LJSONObject.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(FIELD_PROPERTY_DATA_TYPE, TJSONString.Create(GetEnumName(TypeInfo(TFieldType), Integer(LField.DataType))));
    LJSONObject.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(FIELD_PROPERTY_SIZE, {$IF DEFINED(FPC)}LField.Size{$ELSE}TJSONNumber.Create(LField.Size){$ENDIF});
    LJSONObject.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(FIELD_PROPERTY_ORIGIN, TJSONString.Create(LField.ORIGIN));

    if IsPublishedProp(LField, 'Precision') then
      LJSONObject.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(FIELD_PROPERTY_PRECISION, {$IF DEFINED(FPC)}TFloatField(LField).Precision{$ELSE}TJSONNumber.Create(TFloatField(LField).Precision){$ENDIF});

    {$IF DEFINED(FPC)}
    LJSONObject.Add(FIELD_PROPERTY_KEY, pfInKey in LField.ProviderFlags);
    LJSONObject.Add(FIELD_PROPERTY_REQUIRED, LField.Required);
    LJSONObject.Add(FIELD_PROPERTY_VISIBLE, LField.Visible);
    LJSONObject.Add(FIELD_PROPERTY_READ_ONLY, LField.ReadOnly);
    {$ELSE}
      {$IF COMPILERVERSION <= 29}
      LJSONObject.AddPair(FIELD_PROPERTY_KEY, TJSONString.Create(BoolToStr(pfInKey in LField.ProviderFlags)));
      LJSONObject.AddPair(FIELD_PROPERTY_REQUIRED, TJSONString.Create(BoolToStr(LField.Required)));
      LJSONObject.AddPair(FIELD_PROPERTY_VISIBLE, TJSONString.Create(BoolToStr(LField.Visible)));
      LJSONObject.AddPair(FIELD_PROPERTY_READ_ONLY, TJSONString.Create(BoolToStr(LField.ReadOnly)));
      {$ELSE}
      LJSONObject.AddPair(FIELD_PROPERTY_KEY, TJSONBool.Create(pfInKey in LField.ProviderFlags));
      LJSONObject.AddPair(FIELD_PROPERTY_REQUIRED, TJSONBool.Create(LField.Required));
      LJSONObject.AddPair(FIELD_PROPERTY_VISIBLE, TJSONBool.Create(LField.Visible));
      LJSONObject.AddPair(FIELD_PROPERTY_READ_ONLY, TJSONBool.Create(LField.ReadOnly));
      {$ENDIF}
    {$ENDIF}

    {$IF NOT DEFINED(FPC)}
    LJSONObject.AddPair(FIELD_PROPERTY_AUTO_GENERATE_VALUE, TJSONString.Create(GetEnumName(TypeInfo(TAutoRefreshFlag), Integer(LField.AutoGenerateValue))));
    {$ENDIF}
    Result.{$IF DEFINED(FPC)}Add{$ELSE}AddElement{$ENDIF}(LJSONObject);
  end;
end;

constructor TDataSetSerialize.Create(const ADataSet: TDataSet; const AOnlyUpdatedRecords: Boolean = False; const AChildRecords: Boolean = True; const AValueRecords: Boolean = true);
begin
  FDataSet := ADataSet;
  FOnlyUpdatedRecords := AOnlyUpdatedRecords;
  FChildRecord := AChildRecords;
  FValueRecord := AValueRecords;
end;

function TDataSetSerialize.ToJSONArray: TJSONArray;
begin
  Result := DataSetToJSONArray(FDataSet, FChildRecord, FValueRecord);
end;

end.

DataSet.Serialize

unit DataSet.Serialize;

{$IF DEFINED(FPC)}
  {$MODE DELPHI}{$H+}
{$ENDIF}

interface

uses
{$IF DEFINED(FPC)}
  DB, fpjson,
{$ELSE}
  System.JSON, Data.DB,
{$ENDIF}
  DataSet.Serialize.Language, DataSet.Serialize.Config;

type
  TLanguageType = DataSet.Serialize.Language.TLanguageType;
  TDataSetSerializeConfig = DataSet.Serialize.Config.TDataSetSerializeConfig;
  TCaseNameDefinition = DataSet.Serialize.Config.TCaseNameDefinition;

  TDataSetSerializeHelper = class Helper for TDataSet
  public
    /// <summary>
    ///   Creates a JSON object with the data from the current record of DataSet.
    /// </summary>
    /// <param name="AOnlyUpdatedRecords">
    ///   Exports only inserted, modified and deleted data from childs dataset.
    /// </param>
    /// <param name="AChildRecords">
    ///   Exports only childs records from child datasets.
    /// </param>
    /// <returns>
    ///   Returns a JSON string containing the record data.
    /// </returns>
    /// <remarks>
    ///   Invisible fields will not be generated.
    /// </remarks>
    function ToJSONObjectString(const AOnlyUpdatedRecords: Boolean = False; const AChildRecords: Boolean = True): string;
    /// <summary>
    ///   Creates an array of JSON objects with all DataSet records.
    /// </summary>
    /// <param name="AOnlyUpdatedRecords">
    ///   Exports only inserted, modified and deleted data from childs dataset.
    /// </param>
    /// <param name="AChildRecords">
    ///   Exports only childs records from child datasets.
    /// </param>
    /// <returns>
    ///   Returns a JSON string with all records from the DataSet.
    /// </returns>
    /// <remarks>
    ///   Invisible fields will not be generated.
    /// </remarks>
    function ToJSONArrayString(const AOnlyUpdatedRecords: Boolean = False; const AChildRecords: Boolean = True): string;
    /// <summary>
    ///   Creates a JSON object with the data from the current record of DataSet.
    /// </summary>
    /// <param name="AOnlyUpdatedRecords">
    ///   Exports only inserted, modified and deleted data from childs dataset.
    /// </param>
    /// <param name="AChildRecords">
    ///   Exports only childs records from child datasets.
    /// </param>
    /// <returns>
    ///   Returns a JSON object containing the record data.
    /// </returns>
    /// <remarks>
    ///   Invisible fields will not be generated.
    /// </remarks>
    function ToJSONObject(const AOnlyUpdatedRecords: Boolean = False; const AChildRecords: Boolean = True): TJSONObject;
    /// <summary>
    ///   Creates an array of JSON objects with all DataSet records.
    /// </summary>
    /// <param name="AOnlyUpdatedRecords">
    ///   Exports only inserted, modified and deleted data from childs dataset.
    /// </param>
    /// <param name="AChildRecords">
    ///   Exports only childs records from child datasets.
    /// </param>
    /// <returns>
    ///   Returns a JSONArray with all records from the DataSet.
    /// </returns>
    /// <remarks>
    ///   Invisible fields will not be generated.
    /// </remarks>
    function ToJSONArray(const AOnlyUpdatedRecords: Boolean = False; const AChildRecords: Boolean = True; AValueRecords: Boolean = true): TJSONArray;
    /// <summary>
    ///   Responsible for exporting the structure of a DataSet in JSON Array format.
    /// </summary>
    /// <returns>
    ///   Returns a JSON array with all fields of the DataSet.
    /// </returns>
    /// <remarks>
    ///   Invisible fields will not be generated.
    /// </remarks>
    function SaveStructure: TJSONArray;
    /// <summary>
    ///   Loads fields from a DataSet based on a JSONArray.
    /// </summary>
    /// <param name="AJSONArray">
    ///   Refers to JSON with field specifications.
    /// </param>
    /// <param name="AOwns">
    ///   Destroy JSON in the end.
    /// </param>
    procedure LoadStructure(const AJSONArray: TJSONArray; const AOwns: Boolean = True); overload;
    /// <summary>
    ///   Loads fields from a DataSet based on a JSON (string format).
    /// </summary>
    /// <param name="AJSONString">
    ///   Refers to JSON with field specifications.
    /// </param>
    procedure LoadStructure(const AJSONString: string); overload;
    /// <summary>
    ///   Loads the DataSet with data from a JSON object.
    /// </summary>
    /// <param name="AJSONObject">
    ///   Refers to JSON that you want to load.
    /// </param>
    /// <param name="AOwns">
    ///   Destroy JSON in the end.
    /// </param>
    /// <remarks>
    ///   Only the keys that make up the DataSet field list will be loaded. The JSON keys must have the same name as the
    ///   DataSet fields. It's not case-sensitive.
    /// </remarks>
    procedure LoadFromJSON(const AJSONObject: TJSONObject; const AOwns: Boolean = True); overload;
    /// <summary>
    ///   Loads the DataSet with data from a JSON array.
    /// </summary>
    /// <param name="AJSONArray">
    ///   Refers to JSON that you want to load.
    /// </param>
    /// <param name="AOwns">
    ///   Destroy JSON in the end.
    /// </param>
    /// <remarks>
    ///   Only the keys that make up the DataSet field list will be loaded. The JSON keys must have the same name as the
    ///   DataSet fields. It's not case-sensitive.
    /// </remarks>
    procedure LoadFromJSON(const AJSONArray: TJSONArray; const AOwns: Boolean = True); overload;
    /// <summary>
    ///   Loads the DataSet with data from a JSON (string format).
    /// </summary>
    /// <param name="AJSONString">
    ///   Refers to JSON that you want to load.
    /// </param>
    /// <remarks>
    ///   Only the keys that make up the DataSet field list will be loaded. The JSON keys must have the same name as the
    ///   DataSet fields. It's not case-sensitive.
    /// </remarks>
    procedure LoadFromJSON(const AJSONString: string); overload;
    /// <summary>
    ///   Updates the DataSet data with JSON object data.
    /// </summary>
    /// <param name="AJSONObject">
    ///   Refers to JSON that you want to merge.
    /// </param>
    /// <param name="AOwns">
    ///   Destroy JSON in the end.
    /// </param>
    procedure MergeFromJSONObject(const AJSONObject: TJSONObject; const AOwns: Boolean = True); overload;
    /// <summary>
    ///   Updates the DataSet data with JSON object data (string format).
    /// </summary>
    /// <param name="AJSONString">
    ///   Refers to JSON that you want to merge.
    /// </param>
    procedure MergeFromJSONObject(const AJSONString: string); overload;
    /// <summary>
    ///   Responsible for validating whether JSON has all the necessary information for a particular DataSet.
    /// </summary>
    /// <param name="AJSONObject">
    ///   Refers to JSON that must be validated.
    /// </param>
    /// <param name="ALang">
    ///   Language used to mount messages.
    /// </param>
    /// <param name="AOwns">
    ///   Destroy JSON in the end.
    /// </param>
    /// <returns>
    ///   Returns a JSONArray with the fields that were not informed.
    /// </returns>
    /// <remarks>
    ///   Walk the DataSet fields by checking the required property.
    ///   Uses the DisplayLabel property to mount the message.
    /// </remarks>
    function ValidateJSON(const AJSONObject: TJSONObject; const ALang: TLanguageType = enUS; const AOwns: Boolean = True): TJSONArray; overload;
    /// <summary>
    ///   Responsible for validating whether JSON has all the necessary information for a particular DataSet.
    /// </summary>
    /// <param name="AJSONString">
    ///   Refers to JSON that must be validated.
    /// </param>
    /// <param name="ALang">
    ///   Language used to mount messages.
    /// </param>
    /// <returns>
    ///   Returns a JSONArray with the fields that were not informed.
    /// </returns>
    /// <remarks>
    ///   Walk the DataSet fields by checking the required property.
    ///   Uses the DisplayLabel property to mount the message.
    /// </remarks>
    function ValidateJSON(const AJSONString: string; const ALang: TLanguageType = enUS): TJSONArray; overload;
  end;

implementation

uses
{$IF DEFINED(FPC)}
  SysUtils,
{$ELSE}
  System.SysUtils,
{$ENDIF}
  DataSet.Serialize.Export, DataSet.Serialize.Import;

function TDataSetSerializeHelper.ToJSONArray(const AOnlyUpdatedRecords: Boolean = False; const AChildRecords: Boolean = True; AValueRecords: Boolean = true): TJSONArray;
var
  LDataSetSerialize: TDataSetSerialize;
begin
  LDataSetSerialize := TDataSetSerialize.Create(Self, AOnlyUpdatedRecords, AChildRecords, AValueRecords);
  try
    Result := LDataSetSerialize.ToJSONArray;
  finally
    LDataSetSerialize.Free;
  end;
end;

function TDataSetSerializeHelper.ToJSONObject(const AOnlyUpdatedRecords: Boolean = False; const AChildRecords: Boolean = True): TJSONObject;
var
  LDataSetSerialize: TDataSetSerialize;
begin
  LDataSetSerialize := TDataSetSerialize.Create(Self, AOnlyUpdatedRecords, AChildRecords);
  try
    Result := LDataSetSerialize.ToJSONObject;
  finally
    LDataSetSerialize.Free;
  end;
end;

function TDataSetSerializeHelper.ToJSONObjectString(const AOnlyUpdatedRecords: Boolean = False; const AChildRecords: Boolean = True): string;
var
  LJSONObject: TJSONObject;
begin
  LJSONObject := Self.ToJSONObject(AOnlyUpdatedRecords, AChildRecords);
  try
    Result := {$IF DEFINED(FPC)}LJSONObject.AsJSON{$ELSE}LJSONObject.ToString{$ENDIF};
  finally
    LJSONObject.Free;
  end;
end;

function TDataSetSerializeHelper.ToJSONArrayString(const AOnlyUpdatedRecords: Boolean = False; const AChildRecords: Boolean = True): string;
var
  LJSONArray: TJSONArray;
begin
  LJSONArray := Self.ToJSONArray(AOnlyUpdatedRecords, AChildRecords);
  try
    Result := {$IF DEFINED(FPC)}LJSONArray.AsJSON{$ELSE}LJSONArray.ToString{$ENDIF};
  finally
    LJSONArray.Free;
  end;
end;

function TDataSetSerializeHelper.SaveStructure: TJSONArray;
var
  LDataSetSerialize: TDataSetSerialize;
begin
  LDataSetSerialize := TDataSetSerialize.Create(Self);
  try
    Result := LDataSetSerialize.SaveStructure;
  finally
    LDataSetSerialize.Free;
  end;
end;

function TDataSetSerializeHelper.ValidateJSON(const AJSONObject: TJSONObject; const ALang: TLanguageType = enUS; const AOwns: Boolean = True): TJSONArray;
var
  LJSONSerialize: TJSONSerialize;
begin
  LJSONSerialize := TJSONSerialize.Create(AJSONObject, AOwns);
  try
    Result := LJSONSerialize.Validate(Self, ALang);
  finally
    LJSONSerialize.Free;
  end;
end;

procedure TDataSetSerializeHelper.LoadFromJSON(const AJSONArray: TJSONArray; const AOwns: Boolean = True);
var
  LJSONSerialize: TJSONSerialize;
begin
  LJSONSerialize := TJSONSerialize.Create(AJSONArray, AOwns);
  try
    LJSONSerialize.ToDataSet(Self);
  finally
    LJSONSerialize.Free;
  end;
end;

procedure TDataSetSerializeHelper.LoadFromJSON(const AJSONObject: TJSONObject; const AOwns: Boolean = True);
var
  LJSONSerialize: TJSONSerialize;
begin
  LJSONSerialize := TJSONSerialize.Create(AJSONObject, AOwns);
  try
    LJSONSerialize.ToDataSet(Self);
  finally
    LJSONSerialize.Free;
  end;
end;

procedure TDataSetSerializeHelper.LoadStructure(const AJSONArray: TJSONArray; const AOwns: Boolean = True);
var
  LJSONSerialize: TJSONSerialize;
begin
  LJSONSerialize := TJSONSerialize.Create(AJSONArray, AOwns);
  try
    LJSONSerialize.LoadStructure(Self);
  finally
    LJSONSerialize.Free;
  end;
end;

procedure TDataSetSerializeHelper.MergeFromJSONObject(const AJSONObject: TJSONObject; const AOwns: Boolean = True);
var
  LJSONSerialize: TJSONSerialize;
begin
  LJSONSerialize := TJSONSerialize.Create(AJSONObject, AOwns);
  try
    LJSONSerialize.Merge(Self);
  finally
    LJSONSerialize.Free;
  end;
end;

function TDataSetSerializeHelper.ValidateJSON(const AJSONString: string; const ALang: TLanguageType): TJSONArray;
begin
  if Trim(AJSONString).StartsWith('{') then
    Result := ValidateJSON({$IF DEFINED(FPC)}GetJSON(AJSONString){$ELSE}TJSONObject.ParseJSONValue(TEncoding.UTF8.GetBytes(AJSONString), 0){$ENDIF} as TJSONObject, ALang)
  else
    Result := TJSONArray.Create();
end;

procedure TDataSetSerializeHelper.LoadFromJSON(const AJSONString: string);
begin
  if Trim(AJSONString).StartsWith('{') then
    LoadFromJSON({$IF DEFINED(FPC)}GetJSON(AJSONString){$ELSE}TJSONObject.ParseJSONValue(TEncoding.UTF8.GetBytes(AJSONString), 0){$ENDIF} as TJSONObject)
  else if Trim(AJSONString).StartsWith('[') then
    LoadFromJSON({$IF DEFINED(FPC)}GetJSON(AJSONString){$ELSE}TJSONObject.ParseJSONValue(TEncoding.UTF8.GetBytes(AJSONString), 0){$ENDIF} as TJSONArray);
end;

procedure TDataSetSerializeHelper.LoadStructure(const AJSONString: string);
begin
  if Trim(AJSONString).StartsWith('[') then
    LoadStructure({$IF DEFINED(FPC)}GetJSON(AJSONString){$ELSE}TJSONObject.ParseJSONValue(TEncoding.UTF8.GetBytes(AJSONString), 0){$ENDIF} as TJSONArray);
end;

procedure TDataSetSerializeHelper.MergeFromJSONObject(const AJSONString: string);
begin
  if Trim(AJSONString).StartsWith('{') then
    MergeFromJSONObject({$IF DEFINED(FPC)}GetJSON(AJSONString){$ELSE}TJSONObject.ParseJSONValue(TEncoding.UTF8.GetBytes(AJSONString), 0){$ENDIF} as TJSONObject)
end;

end.

Missing prerequisites section in README

SUGGESTION:
You can add (maybe to the prerequisites section), Delphi versions supported for this project.

P.D. This suggestion applies to all your other Pascal projects too ;-)

Nested JSON support using query

Today dataset-serialize allows you to export and import nested JSON using a CDS / MemTable ... components that have the DataSetField property. For components with database access such as an FDQuery, you do not have this possibility.
Implement support for using FDQuery with nested JSON, for exporting, and for importing.

Bug ToJSONArray

Hello,
if you have a dataset with only one field (idalbc) an for example 3 records the result for ToJSONArray is:

[
"23920",
"23921",
"23922"
]
is wrong

while if you have two fields (idalbc,fecha) the result is correct
[
{
"idalbc": "23920",
"fecha": "2022-04-07 18:26:04.000"
},
{
"idalbc": "23921",
"fecha": "2022-04-07 18:34:29.000"
},
{
"idalbc": "23922",
"fecha": "2022-04-11 17:05:04.000"
}
]

FormatCurrency ignorado no MySQL

O FormatCurrency é ignorado para campos DOUBLE (12,4) no MySQL.
Dentro do Delphi o campo é identificado como TFieldType.ftSingle

Importação de um Array

Prezados Vicente e Vinicius, boa noite.

Estou com uma dificuldade que acredito que seja simples de resolver.

Na importação de um JSON Array a função LoadFromJSON não está conseguindo ler o JSON quando o mesmo trás um título no inicio do JSON:
O exemplo 1 não importar para o DBGrid.
Já o Exemplo 2 importa 100% das linhas do Array, porém a API que estou Consumindo, todas tem o título no inicio do JSON.

Como faço para conseguir importar fazendo com que o DataSet-Serialize possa identidicar o título?

Exemplo 1:
{
users:[
{
"id": 1,
"name": "Mateus Vicente",
"country": "Brazil"
},
{
"id": 2,
"name": "Vinicius Sanchez",
"country": "Brazil"
}
]
}

Exemplo 2:

[
{
"id": 1,
"name": "Mateus Vicente",
"country": "Brazil"
},
{
"id": 2,
"name": "Vinicius Sanchez",
"country": "Brazil"
}
]

Desde já agradeço.

Melhor método para consumir APIs com estruturas JSON complexas

Estou tendo dificuldade pra agregar o DataSetAdapter a um Dataset (MemTable) em estruturas com um JSON muito extensa com arrays e objetos encadeados. Não tenho problemas para obter a resposta por inteiro, apenas armazená-la no dataset.

No caso do JSON abaixo, apenas as chaves do objeto principal (error, message, warning, request_id, response) são exibidas numa dbgrid, por exemplo.
E como neste caso, o response é um objeto, ele não exibe seu valor, apenas a chave.
Qual seria a melhor abordagem para uma estrutura como essa?
Sei que tem como obter o valor específico de uma chave com a função GetValue do JsonValue.
Só gostaria de saber se existe uma abordagem mais simples, como a do Root Element do RestResponseDataSetAdapter.

Um exemplo de um json:

    "error": "",
    "message": "",
    "warning": "",
    "request_id": "72bef80ffc56e3c559bf4da951d2739a",
    "response": {
        "item_list": [
            {
                "item_id": 100020959,
                "category_id": 24283,
                "item_name": "Mouse Dell",
                "description": "Mouse Dell Preto",
                "item_sku": "123667",
                "create_time": 1626203470,
                "update_time": 1626282375,
                "price_info": [
                    {
                        "currency": "BRL",
                        "original_price": 50,
                        "current_price": 50
                    }
                ],
                "stock_info": [
                    {
                        "stock_type": 2,
                        "current_stock": 100,
                        "normal_stock": 100,
                        "reserved_stock": 0
                    }
                ],
                "image": {
                    "image_url_list": [
                        "https://cf.shopee.com.br/file/f9c7b96be7eca79e4e84b22ec864e791"
                    ],
                    "image_id_list": [
                        "f9c7b96be7eca79e4e84b22ec864e791"
                    ]
                },
                "weight": "0.300",
                "dimension": {
                    "package_length": 20,
                    "package_width": 40,
                    "package_height": 3
                },
                "logistic_info": [
                    {
                        "logistic_id": 90003,
                        "logistic_name": "Padrão",
                        "enabled": true,
                        "is_free": false,
                        "estimated_shipping_fee": 18.1
                    }
                ],
                "pre_order": {
                    "is_pre_order": false,
                    "days_to_ship": 3
                },
                "condition": "NEW",
                "size_chart": "",
                "item_status": "NORMAL",
                "has_model": false,
                "promotion_id": 0,
                "brand": {
                    "brand_id": 0,
                    "original_brand_name": "Dell"
                },
                "item_dangerous": 0
            }
        ]
    }
}

TDD

Implement TDD

Padrão lowerCamelCase no nome do DataSet

Na alinha 209 da unit DataSet.Serialize.Export está dando um LowerCase no nome do DataSet, não respeitando o o padrão LowerCamelCase (deve respeitar o parâmetro para isso).

Result.AddPair(LowerCase(TDataSetSerializeUtils.FormatDataSetName(LNestedDataSet.Name)), DataSetToJSONArray(LNestedDataSet, True));

Date locale conversion issue in configuration sample

Hi Vinicius,

in the "configuration" sample is an local issue, right when starting and populating the database.
Since in Germany the date locale is "." instead of "/" I prepared a short workaround, see below:

`

 //S4: 22.09.21 Workaround for locale de-DE, with . instead of / for date strings
  if FormatSettings.DateSeparator = '.' then
  begin
      mtUsers.AppendRecord([1, 'Mateus Vicente', '13.04.1998', 14999.99]);
      mtUsers.AppendRecord([2, 'Vinicius Sanchez', '03.08.1995', Null]);
      mtUsers.AppendRecord([3, 'Julio Senha', '04.06.1985', 27000.00]);
      mtUsers.AppendRecord([4, 'Fagner Granela', Null, 105000.00]);
  end
  else
  begin
      mtUsers.AppendRecord([1, 'Mateus Vicente', '13/04/1998', 14999.99]);
      mtUsers.AppendRecord([2, 'Vinicius Sanchez', '03/08/1995', Null]);
      mtUsers.AppendRecord([3, 'Julio Senha', '04/06/1985', 27000.00]);
      mtUsers.AppendRecord([4, 'Fagner Granela', Null, 105000.00]);
  end;

`

Um campo apenas para serializar

Ao serializar apenas de um campo o retorno é o valor do campo apenas, modifiquei para trazer o registro serializado se for informado o argumento AValueRecords = false.

Modificado as seguintes units

DataSet.Serialize.Export

unit DataSet.Serialize.Export;

{$IF DEFINED(FPC)}
  {$MODE DELPHI}{$H+}
{$ENDIF}

interface

uses
{$IF DEFINED(FPC)}
  DB, fpjson;
{$ELSE}
  Data.DB, System.JSON;
{$ENDIF}

type
  TDataSetSerialize = class
  private
    FDataSet: TDataSet;
    FOnlyUpdatedRecords: Boolean;
    FChildRecord: Boolean;
    FValueRecord: Boolean;
    /// <summary>
    ///   Creates a JSON object with the data from the current record of DataSet.
    /// </summary>
    /// <param name="ADataSet">
    ///   Refers to the DataSet that you want to export the record.
    /// </param>
    /// <returns>
    ///   Returns a JSON object containing the record data.
    /// </returns>
    /// <remarks>
    ///   Invisible or null fields will not be exported.
    /// </remarks>
    function DataSetToJSONObject(const ADataSet: TDataSet; const AValue: Boolean = true): TJSONObject;
    /// <summary>
    ///   Creates an array of JSON objects with all DataSet records.
    /// </summary>
    /// <param name="ADataSet">
    ///   Refers to the DataSet that you want to export the records.
    /// </param>
    /// <returns>
    ///   Returns a JSONArray with all records from the DataSet.
    /// </returns>
    /// <remarks>
    ///   Invisible or null fields will not be exported.
    /// </remarks>
    function DataSetToJSONArray(const ADataSet: TDataSet; const IsChild: Boolean; const isValue: Boolean = true ): TJSONArray;
    /// <summary>
    ///   Encrypts a blob field in Base64.
    /// </summary>
    /// <param name="AField">
    ///   Refers to the field of type Blob or similar.
    /// </param>
    /// <returns>
    ///   Returns a string with the cryptogrammed content in Base64.
    /// </returns>
    function EncodingBlobField(const AField: TField): string;
    {$IF NOT DEFINED(FPC)}
    /// <summary>
    ///   Verifiy if a DataSet has detail dataset and if has child modification.
    /// </summary>
    function HasChildModification(const ADataSet: TDataSet): Boolean;
    {$ENDIF}
  public
    /// <summary>
    ///   Responsible for creating a new instance of TDataSetSerialize class.
    /// </summary>
    constructor Create(const ADataSet: TDataSet; const AOnlyUpdatedRecords: Boolean = False; const AChildRecords: Boolean = True; const AValueRecords: Boolean = true);
    /// <summary>
    ///   Creates an array of JSON objects with all DataSet records.
    /// </summary>
    /// <returns>
    ///   Returns a JSONArray with all records from the DataSet.
    /// </returns>
    /// <remarks>
    ///   Invisible or null fields will not be generated.
    /// </remarks>
    function ToJSONArray: TJSONArray;
    /// <summary>
    ///   Creates a JSON object with the data from the current record of DataSet.
    /// </summary>
    /// <returns>
    ///   Returns a JSON object containing the record data.
    /// </returns>
    /// <remarks>
    ///   Invisible or null fields will not be generated.
    /// </remarks>
    function ToJSONObject: TJSONObject;
    /// <summary>
    ///   Responsible for exporting the structure of a DataSet in JSON Array format.
    /// </summary>
    /// <returns>
    ///   Returns a JSON array with all fields of the DataSet.
    /// </returns>
    /// <remarks>
    ///   Invisible fields will not be generated.
    /// </remarks>
    function SaveStructure: TJSONArray;
  end;

implementation

uses
{$IF DEFINED(FPC)}
  DateUtils, SysUtils, Classes, FmtBCD, TypInfo, base64,
{$ELSE}
  System.DateUtils, Data.FmtBcd, System.SysUtils, System.TypInfo, System.Classes, System.NetEncoding, System.Generics.Collections,
  FireDAC.Comp.DataSet,
{$ENDIF}
  DataSet.Serialize.Utils, DataSet.Serialize.Consts, DataSet.Serialize.UpdatedStatus, DataSet.Serialize.Config;

{ TDataSetSerialize }

function TDataSetSerialize.ToJSONObject: TJSONObject;
begin
  Result := DataSetToJSONObject(FDataSet);
end;

function TDataSetSerialize.DataSetToJSONArray(const ADataSet: TDataSet; const IsChild: Boolean; const isValue: Boolean = true): TJSONArray;
var
  LBookMark: TBookmark;
begin
  Result := TJSONArray.Create;
  if ADataSet.IsEmpty then
    Exit;
  try
    LBookMark := ADataSet.BookMark;
    ADataSet.First;
    while not ADataSet.Eof do
    begin
      {$IF DEFINED(FPC)}
      Result.Add(DataSetToJSONObject(ADataSet));
      {$ELSE}
      if IsChild and FOnlyUpdatedRecords then
        if (ADataSet.UpdateStatus = TUpdateStatus.usUnmodified) and not(HasChildModification(ADataSet)) then
        begin
          ADataSet.Next;
          Continue;
        end;
      if (ADataSet.FieldCount = 1)  and (isValue)  then
      begin
        case ADataSet.Fields[0].DataType of
          TFieldType.ftBoolean:
            Result.Add(ADataSet.Fields[0].AsBoolean);
          TFieldType.ftInteger, TFieldType.ftSmallint, TFieldType.ftShortint:
            Result.Add(ADataSet.Fields[0].AsInteger);
          TFieldType.ftLongWord, TFieldType.ftAutoInc, TFieldType.ftString, TFieldType.ftWideString, TFieldType.ftMemo, TFieldType.ftWideMemo, TFieldType.ftGuid:
            Result.Add(ADataSet.Fields[0].AsWideString);
          TFieldType.ftLargeint:
            Result.Add(ADataSet.Fields[0].AsLargeInt);
          TFieldType.ftSingle, TFieldType.ftFloat:
            Result.Add(ADataSet.Fields[0].AsFloat);
          TFieldType.ftDateTime:
               Result.Add(FormatDateTime(TDataSetSerializeConfig.GetInstance.Export.FormatDateTime, ADataSet.Fields[0].AsDateTime));
           TFieldType.ftTimeStamp:
               Result.Add(DateToISO8601(ADataSet.Fields[0].AsDateTime, TDataSetSerializeConfig.GetInstance.DateInputIsUTC));
           TFieldType.ftTime:
               Result.Add(FormatDateTime(TDataSetSerializeConfig.GetInstance.Export.FormatTime, ADataSet.Fields[0].AsDateTime));
           TFieldType.ftDate:
               Result.Add(FormatDateTime(TDataSetSerializeConfig.GetInstance.Export.FormatDate, ADataSet.Fields[0].AsDateTime));
          TFieldType.ftCurrency:
            begin
              if TDataSetSerializeConfig.GetInstance.Export.FormatCurrency.Trim.IsEmpty then
                Result.Add(ADataSet.Fields[0].AsCurrency)
              else
                Result.Add(FormatCurr(TDataSetSerializeConfig.GetInstance.Export.FormatCurrency, ADataSet.Fields[0].AsCurrency));
            end;
          TFieldType.ftFMTBcd, TFieldType.ftBCD:
            Result.Add(BcdToDouble(ADataSet.Fields[0].AsBcd));
          TFieldType.ftGraphic, TFieldType.ftBlob, TFieldType.ftOraBlob, TFieldType.ftStream:
            Result.Add(EncodingBlobField(ADataSet.Fields[0]));
          else
            raise EDataSetSerializeException.CreateFmt(FIELD_TYPE_NOT_FOUND, [ADataSet.Fields[0].FieldName]);
        end;
      end
      else
        Result.AddElement(DataSetToJSONObject(ADataSet));
      {$ENDIF}
      ADataSet.Next;
    end;
  finally
    if ADataSet.BookmarkValid(LBookMark) then
      ADataSet.GotoBookmark(LBookMark);
    ADataSet.FreeBookmark(LBookMark);
  end;
end;

function TDataSetSerialize.DataSetToJSONObject(const ADataSet: TDataSet; const AValue: Boolean = true): TJSONObject;
var
  LKey: string;
  {$IF NOT DEFINED(FPC)}
  LNestedDataSet: TDataSet;
  LDataSetDetails: TList<TDataSet>;
  {$ENDIF}
  LField: TField;
begin
  Result := TJSONObject.Create;
  if not Assigned(ADataSet) or ADataSet.IsEmpty then
    Exit;
  for LField in ADataSet.Fields do
  begin
    if TDataSetSerializeConfig.GetInstance.Export.ExportOnlyFieldsVisible then
      if not(LField.Visible) then
        Continue;
    LKey := TDataSetSerializeUtils.FormatCaseNameDefinition(LField.FieldName);
    if LField.IsNull then
    begin
      if TDataSetSerializeConfig.GetInstance.Export.ExportNullValues then
        if TDataSetSerializeConfig.GetInstance.Export.ExportNullAsEmptyString then
          Result.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(LKey, '')
        else
          Result.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(LKey, TJSONNull.Create);
      Continue;
    end;
    case LField.DataType of
      TFieldType.ftBoolean:
        Result.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(LKey, TDataSetSerializeUtils.BooleanToJSON(LField.AsBoolean));
      TFieldType.ftInteger, TFieldType.ftSmallint{$IF NOT DEFINED(FPC)}, TFieldType.ftShortint, TFieldType.ftWord, TFieldType.ftByte{$ENDIF}:
        Result.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(LKey, {$IF DEFINED(FPC)}LField.AsInteger{$ELSE}TJSONNumber.Create(LField.AsInteger){$ENDIF});
      {$IF NOT DEFINED(FPC)}TFieldType.ftLongWord, {$ENDIF}TFieldType.ftAutoInc:
        Result.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(LKey, {$IF DEFINED(FPC)}LField.AsWideString{$ELSE}TJSONNumber.Create(LField.AsWideString){$ENDIF});
      TFieldType.ftLargeint:
        Result.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(LKey, {$IF DEFINED(FPC)}LField.AsLargeInt{$ELSE}TJSONNumber.Create(LField.AsLargeInt){$ENDIF});
      {$IF NOT DEFINED(FPC)}TFieldType.ftSingle, TFieldType.ftExtended, {$ENDIF}TFieldType.ftFloat:
        Result.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(LKey, {$IF DEFINED(FPC)}LField.AsFloat{$ELSE}TJSONNumber.Create(LField.AsFloat){$ENDIF});
      TFieldType.ftString, TFieldType.ftWideString, TFieldType.ftMemo, TFieldType.ftWideMemo, TFieldType.ftGuid, TFieldType.ftFixedChar, TFieldType.ftFixedWideChar:
        Result.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(LKey, TJSONString.Create(LField.AsWideString));
      TFieldType.ftDateTime:
           Result.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(LKey, TJSONString.Create(FormatDateTime(TDataSetSerializeConfig.GetInstance.Export.FormatDateTime, LField.AsDateTime)));
       TFieldType.ftTimeStamp:
           Result.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(LKey, TJSONString.Create(DateToISO8601(LField.AsDateTime, TDataSetSerializeConfig.GetInstance.DateInputIsUTC)));
       TFieldType.ftTime:
           Result.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(LKey, TJSONString.Create(FormatDateTime(TDataSetSerializeConfig.GetInstance.Export.FormatTime, LField.AsDateTime)));
       TFieldType.ftDate:
           Result.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(LKey, TJSONString.Create(FormatDateTime(TDataSetSerializeConfig.GetInstance.Export.FormatDate, LField.AsDateTime)));
      TFieldType.ftCurrency:
        begin
          if TDataSetSerializeConfig.GetInstance.Export.FormatCurrency.Trim.IsEmpty then
            Result.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(LKey, {$IF DEFINED(FPC)}LField.AsCurrency{$ELSE}TJSONNumber.Create(LField.AsCurrency){$ENDIF})
          else
            Result.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(LKey, TJSONString.Create(FormatCurr(TDataSetSerializeConfig.GetInstance.Export.FormatCurrency, LField.AsCurrency)));
        end;
      TFieldType.ftFMTBcd, TFieldType.ftBCD:
        Result.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(LKey, {$IF DEFINED(FPC)}BcdToDouble(LField.AsBcd){$ELSE}TJSONNumber.Create(BcdToDouble(LField.AsBcd)){$ENDIF});
      {$IF NOT DEFINED(FPC)}
      TFieldType.ftDataSet:
        begin
          LNestedDataSet := TDataSetField(LField).NestedDataSet;
          Result.AddPair(LKey, DataSetToJSONArray(LNestedDataSet, AValue));
        end;
      {$ENDIF}
      TFieldType.ftGraphic, TFieldType.ftBlob, TFieldType.ftOraBlob{$IF NOT DEFINED(FPC)}, TFieldType.ftStream{$ENDIF}:
        Result.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(LKey, TJSONString.Create(EncodingBlobField(LField)));
      else
        raise EDataSetSerializeException.CreateFmt(FIELD_TYPE_NOT_FOUND, [LKey]);
    end;
  end;
  if (FOnlyUpdatedRecords) and (FDataSet <> ADataSet) then
    Result.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(TDataSetSerializeUtils.FormatCaseNameDefinition('object_state'), TJSONString.Create(ADataSet.UpdateStatus.ToString));
  {$IF NOT DEFINED(FPC)}
  if FChildRecord then
  begin
    LDataSetDetails := TList<TDataSet>.Create;
    try
      ADataSet.GetDetailDataSets(LDataSetDetails);
      for LNestedDataSet in LDataSetDetails do
      begin
        if FOnlyUpdatedRecords then
          TFDDataSet(LNestedDataSet).FilterChanges := [rtInserted, rtModified, rtDeleted, rtUnmodified];
        if TDataSetSerializeConfig.GetInstance.Export.ExportEmptyDataSet or (LNestedDataSet.RecordCount > 0) then
          if TDataSetSerializeConfig.GetInstance.Export.ExportChildDataSetAsJsonObject and (LNestedDataSet.RecordCount = 1) then
            Result.AddPair(TDataSetSerializeUtils.FormatDataSetName(LNestedDataSet.Name), DataSetToJsonObject(LNestedDataSet))
          else
            Result.AddPair(TDataSetSerializeUtils.FormatDataSetName(LNestedDataSet.Name), DataSetToJSONArray(LNestedDataSet, True));
        if FOnlyUpdatedRecords then
          TFDDataSet(LNestedDataSet).FilterChanges := [rtInserted, rtModified, rtUnmodified];
      end;
    finally
      LDataSetDetails.Free;
    end;
  end;
  {$ENDIF}
end;

function TDataSetSerialize.EncodingBlobField(const AField: TField): string;
var
  LMemoryStream: TMemoryStream;
  LStringStream: TStringStream;
  {$IF NOT DEFINED(FPC)}
  LBase64Encoding: TBase64Encoding;
  {$ENDIF}
begin
  LMemoryStream := TMemoryStream.Create;
  LStringStream := TStringStream.Create;
  try
    TBlobField(AField).SaveToStream(LMemoryStream);
    LMemoryStream.Position := 0;
    {$IF DEFINED(FPC)}
    LStringStream.LoadFromStream(LMemoryStream);
    Result := EncodeStringBase64(LStringStream.DataString);
    {$ELSE}
    LBase64Encoding := TBase64Encoding.Create(0);
    try
      LBase64Encoding.Encode(LMemoryStream, LStringStream);
    finally
      LBase64Encoding.Free;
    end;
    Result := LStringStream.DataString;
    {$ENDIF}
  finally
    LStringStream.Free;
    LMemoryStream.Free;
  end;
end;

{$IF NOT DEFINED(FPC)}
function TDataSetSerialize.HasChildModification(const ADataSet: TDataSet): Boolean;
var
  LMasterSource: TDataSource;
  LDataSetDetails: TList<TDataSet>;
  LNestedDataSet: TDataSet;
begin
  Result := False;
  LDataSetDetails := TList<TDataSet>.Create;
  try
    ADataSet.GetDetailDataSets(LDataSetDetails);
    for LNestedDataSet in LDataSetDetails do
    begin
      Result := HasChildModification(LNestedDataSet);
      if Result then
        Break;
      if not (LNestedDataSet is TFDDataSet) then
        Continue;
      LMasterSource := TFDDataSet(LNestedDataSet).MasterSource;
      try
        TFDDataSet(LNestedDataSet).MasterSource := nil;
        TFDDataSet(LNestedDataSet).FilterChanges := [rtInserted, rtModified, rtDeleted];
        Result := TFDDataSet(LNestedDataSet).RecordCount > 0;
        if Result then
          Break;
      finally
        TFDDataSet(LNestedDataSet).FilterChanges := [rtInserted, rtModified, rtUnmodified];
        TFDDataSet(LNestedDataSet).MasterSource := LMasterSource;
      end;
    end;
  finally
    LDataSetDetails.Free;
  end;
end;
{$ENDIF}

function TDataSetSerialize.SaveStructure: TJSONArray;
var
  LField: TField;
  LJSONObject: TJSONObject;
begin
  Result := TJSONArray.Create;
  if FDataSet.FieldCount <= 0 then
    Exit;
  for LField in FDataSet.Fields do
  begin
    LJSONObject := TJSONObject.Create;
    LJSONObject.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(FIELD_PROPERTY_ALIGNMENT, TJSONString.Create(GetEnumName(TypeInfo(TAlignment), Ord(LField.Alignment))));
    LJSONObject.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(FIELD_PROPERTY_FIELD_NAME, TJSONString.Create(LField.FieldName));
    LJSONObject.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(FIELD_PROPERTY_DISPLAY_LABEL, TJSONString.Create(LField.DisplayLabel));
    LJSONObject.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(FIELD_PROPERTY_DATA_TYPE, TJSONString.Create(GetEnumName(TypeInfo(TFieldType), Integer(LField.DataType))));
    LJSONObject.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(FIELD_PROPERTY_SIZE, {$IF DEFINED(FPC)}LField.Size{$ELSE}TJSONNumber.Create(LField.Size){$ENDIF});
    LJSONObject.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(FIELD_PROPERTY_ORIGIN, TJSONString.Create(LField.ORIGIN));

    if IsPublishedProp(LField, 'Precision') then
      LJSONObject.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(FIELD_PROPERTY_PRECISION, {$IF DEFINED(FPC)}TFloatField(LField).Precision{$ELSE}TJSONNumber.Create(TFloatField(LField).Precision){$ENDIF});

    {$IF DEFINED(FPC)}
    LJSONObject.Add(FIELD_PROPERTY_KEY, pfInKey in LField.ProviderFlags);
    LJSONObject.Add(FIELD_PROPERTY_REQUIRED, LField.Required);
    LJSONObject.Add(FIELD_PROPERTY_VISIBLE, LField.Visible);
    LJSONObject.Add(FIELD_PROPERTY_READ_ONLY, LField.ReadOnly);
    {$ELSE}
      {$IF COMPILERVERSION <= 29}
      LJSONObject.AddPair(FIELD_PROPERTY_KEY, TJSONString.Create(BoolToStr(pfInKey in LField.ProviderFlags)));
      LJSONObject.AddPair(FIELD_PROPERTY_REQUIRED, TJSONString.Create(BoolToStr(LField.Required)));
      LJSONObject.AddPair(FIELD_PROPERTY_VISIBLE, TJSONString.Create(BoolToStr(LField.Visible)));
      LJSONObject.AddPair(FIELD_PROPERTY_READ_ONLY, TJSONString.Create(BoolToStr(LField.ReadOnly)));
      {$ELSE}
      LJSONObject.AddPair(FIELD_PROPERTY_KEY, TJSONBool.Create(pfInKey in LField.ProviderFlags));
      LJSONObject.AddPair(FIELD_PROPERTY_REQUIRED, TJSONBool.Create(LField.Required));
      LJSONObject.AddPair(FIELD_PROPERTY_VISIBLE, TJSONBool.Create(LField.Visible));
      LJSONObject.AddPair(FIELD_PROPERTY_READ_ONLY, TJSONBool.Create(LField.ReadOnly));
      {$ENDIF}
    {$ENDIF}

    {$IF NOT DEFINED(FPC)}
    LJSONObject.AddPair(FIELD_PROPERTY_AUTO_GENERATE_VALUE, TJSONString.Create(GetEnumName(TypeInfo(TAutoRefreshFlag), Integer(LField.AutoGenerateValue))));
    {$ENDIF}
    Result.{$IF DEFINED(FPC)}Add{$ELSE}AddElement{$ENDIF}(LJSONObject);
  end;
end;

constructor TDataSetSerialize.Create(const ADataSet: TDataSet; const AOnlyUpdatedRecords: Boolean = False; const AChildRecords: Boolean = True; const AValueRecords: Boolean = true);
begin
  FDataSet := ADataSet;
  FOnlyUpdatedRecords := AOnlyUpdatedRecords;
  FChildRecord := AChildRecords;
  FValueRecord := AValueRecords;
end;

function TDataSetSerialize.ToJSONArray: TJSONArray;
begin
  Result := DataSetToJSONArray(FDataSet, FChildRecord, FValueRecord);
end;

end.

DataSet.Serialize

unit DataSet.Serialize;

{$IF DEFINED(FPC)}
  {$MODE DELPHI}{$H+}
{$ENDIF}

interface

uses
{$IF DEFINED(FPC)}
  DB, fpjson,
{$ELSE}
  System.JSON, Data.DB,
{$ENDIF}
  DataSet.Serialize.Language, DataSet.Serialize.Config;

type
  TLanguageType = DataSet.Serialize.Language.TLanguageType;
  TDataSetSerializeConfig = DataSet.Serialize.Config.TDataSetSerializeConfig;
  TCaseNameDefinition = DataSet.Serialize.Config.TCaseNameDefinition;

  TDataSetSerializeHelper = class Helper for TDataSet
  public
    /// <summary>
    ///   Creates a JSON object with the data from the current record of DataSet.
    /// </summary>
    /// <param name="AOnlyUpdatedRecords">
    ///   Exports only inserted, modified and deleted data from childs dataset.
    /// </param>
    /// <param name="AChildRecords">
    ///   Exports only childs records from child datasets.
    /// </param>
    /// <returns>
    ///   Returns a JSON string containing the record data.
    /// </returns>
    /// <remarks>
    ///   Invisible fields will not be generated.
    /// </remarks>
    function ToJSONObjectString(const AOnlyUpdatedRecords: Boolean = False; const AChildRecords: Boolean = True): string;
    /// <summary>
    ///   Creates an array of JSON objects with all DataSet records.
    /// </summary>
    /// <param name="AOnlyUpdatedRecords">
    ///   Exports only inserted, modified and deleted data from childs dataset.
    /// </param>
    /// <param name="AChildRecords">
    ///   Exports only childs records from child datasets.
    /// </param>
    /// <returns>
    ///   Returns a JSON string with all records from the DataSet.
    /// </returns>
    /// <remarks>
    ///   Invisible fields will not be generated.
    /// </remarks>
    function ToJSONArrayString(const AOnlyUpdatedRecords: Boolean = False; const AChildRecords: Boolean = True): string;
    /// <summary>
    ///   Creates a JSON object with the data from the current record of DataSet.
    /// </summary>
    /// <param name="AOnlyUpdatedRecords">
    ///   Exports only inserted, modified and deleted data from childs dataset.
    /// </param>
    /// <param name="AChildRecords">
    ///   Exports only childs records from child datasets.
    /// </param>
    /// <returns>
    ///   Returns a JSON object containing the record data.
    /// </returns>
    /// <remarks>
    ///   Invisible fields will not be generated.
    /// </remarks>
    function ToJSONObject(const AOnlyUpdatedRecords: Boolean = False; const AChildRecords: Boolean = True): TJSONObject;
    /// <summary>
    ///   Creates an array of JSON objects with all DataSet records.
    /// </summary>
    /// <param name="AOnlyUpdatedRecords">
    ///   Exports only inserted, modified and deleted data from childs dataset.
    /// </param>
    /// <param name="AChildRecords">
    ///   Exports only childs records from child datasets.
    /// </param>
    /// <returns>
    ///   Returns a JSONArray with all records from the DataSet.
    /// </returns>
    /// <remarks>
    ///   Invisible fields will not be generated.
    /// </remarks>
    function ToJSONArray(const AOnlyUpdatedRecords: Boolean = False; const AChildRecords: Boolean = True; AValueRecords: Boolean = true): TJSONArray;
    /// <summary>
    ///   Responsible for exporting the structure of a DataSet in JSON Array format.
    /// </summary>
    /// <returns>
    ///   Returns a JSON array with all fields of the DataSet.
    /// </returns>
    /// <remarks>
    ///   Invisible fields will not be generated.
    /// </remarks>
    function SaveStructure: TJSONArray;
    /// <summary>
    ///   Loads fields from a DataSet based on a JSONArray.
    /// </summary>
    /// <param name="AJSONArray">
    ///   Refers to JSON with field specifications.
    /// </param>
    /// <param name="AOwns">
    ///   Destroy JSON in the end.
    /// </param>
    procedure LoadStructure(const AJSONArray: TJSONArray; const AOwns: Boolean = True); overload;
    /// <summary>
    ///   Loads fields from a DataSet based on a JSON (string format).
    /// </summary>
    /// <param name="AJSONString">
    ///   Refers to JSON with field specifications.
    /// </param>
    procedure LoadStructure(const AJSONString: string); overload;
    /// <summary>
    ///   Loads the DataSet with data from a JSON object.
    /// </summary>
    /// <param name="AJSONObject">
    ///   Refers to JSON that you want to load.
    /// </param>
    /// <param name="AOwns">
    ///   Destroy JSON in the end.
    /// </param>
    /// <remarks>
    ///   Only the keys that make up the DataSet field list will be loaded. The JSON keys must have the same name as the
    ///   DataSet fields. It's not case-sensitive.
    /// </remarks>
    procedure LoadFromJSON(const AJSONObject: TJSONObject; const AOwns: Boolean = True); overload;
    /// <summary>
    ///   Loads the DataSet with data from a JSON array.
    /// </summary>
    /// <param name="AJSONArray">
    ///   Refers to JSON that you want to load.
    /// </param>
    /// <param name="AOwns">
    ///   Destroy JSON in the end.
    /// </param>
    /// <remarks>
    ///   Only the keys that make up the DataSet field list will be loaded. The JSON keys must have the same name as the
    ///   DataSet fields. It's not case-sensitive.
    /// </remarks>
    procedure LoadFromJSON(const AJSONArray: TJSONArray; const AOwns: Boolean = True); overload;
    /// <summary>
    ///   Loads the DataSet with data from a JSON (string format).
    /// </summary>
    /// <param name="AJSONString">
    ///   Refers to JSON that you want to load.
    /// </param>
    /// <remarks>
    ///   Only the keys that make up the DataSet field list will be loaded. The JSON keys must have the same name as the
    ///   DataSet fields. It's not case-sensitive.
    /// </remarks>
    procedure LoadFromJSON(const AJSONString: string); overload;
    /// <summary>
    ///   Updates the DataSet data with JSON object data.
    /// </summary>
    /// <param name="AJSONObject">
    ///   Refers to JSON that you want to merge.
    /// </param>
    /// <param name="AOwns">
    ///   Destroy JSON in the end.
    /// </param>
    procedure MergeFromJSONObject(const AJSONObject: TJSONObject; const AOwns: Boolean = True); overload;
    /// <summary>
    ///   Updates the DataSet data with JSON object data (string format).
    /// </summary>
    /// <param name="AJSONString">
    ///   Refers to JSON that you want to merge.
    /// </param>
    procedure MergeFromJSONObject(const AJSONString: string); overload;
    /// <summary>
    ///   Responsible for validating whether JSON has all the necessary information for a particular DataSet.
    /// </summary>
    /// <param name="AJSONObject">
    ///   Refers to JSON that must be validated.
    /// </param>
    /// <param name="ALang">
    ///   Language used to mount messages.
    /// </param>
    /// <param name="AOwns">
    ///   Destroy JSON in the end.
    /// </param>
    /// <returns>
    ///   Returns a JSONArray with the fields that were not informed.
    /// </returns>
    /// <remarks>
    ///   Walk the DataSet fields by checking the required property.
    ///   Uses the DisplayLabel property to mount the message.
    /// </remarks>
    function ValidateJSON(const AJSONObject: TJSONObject; const ALang: TLanguageType = enUS; const AOwns: Boolean = True): TJSONArray; overload;
    /// <summary>
    ///   Responsible for validating whether JSON has all the necessary information for a particular DataSet.
    /// </summary>
    /// <param name="AJSONString">
    ///   Refers to JSON that must be validated.
    /// </param>
    /// <param name="ALang">
    ///   Language used to mount messages.
    /// </param>
    /// <returns>
    ///   Returns a JSONArray with the fields that were not informed.
    /// </returns>
    /// <remarks>
    ///   Walk the DataSet fields by checking the required property.
    ///   Uses the DisplayLabel property to mount the message.
    /// </remarks>
    function ValidateJSON(const AJSONString: string; const ALang: TLanguageType = enUS): TJSONArray; overload;
  end;

implementation

uses
{$IF DEFINED(FPC)}
  SysUtils,
{$ELSE}
  System.SysUtils,
{$ENDIF}
  DataSet.Serialize.Export, DataSet.Serialize.Import;

function TDataSetSerializeHelper.ToJSONArray(const AOnlyUpdatedRecords: Boolean = False; const AChildRecords: Boolean = True; AValueRecords: Boolean = true): TJSONArray;
var
  LDataSetSerialize: TDataSetSerialize;
begin
  LDataSetSerialize := TDataSetSerialize.Create(Self, AOnlyUpdatedRecords, AChildRecords, AValueRecords);
  try
    Result := LDataSetSerialize.ToJSONArray;
  finally
    LDataSetSerialize.Free;
  end;
end;

function TDataSetSerializeHelper.ToJSONObject(const AOnlyUpdatedRecords: Boolean = False; const AChildRecords: Boolean = True): TJSONObject;
var
  LDataSetSerialize: TDataSetSerialize;
begin
  LDataSetSerialize := TDataSetSerialize.Create(Self, AOnlyUpdatedRecords, AChildRecords);
  try
    Result := LDataSetSerialize.ToJSONObject;
  finally
    LDataSetSerialize.Free;
  end;
end;

function TDataSetSerializeHelper.ToJSONObjectString(const AOnlyUpdatedRecords: Boolean = False; const AChildRecords: Boolean = True): string;
var
  LJSONObject: TJSONObject;
begin
  LJSONObject := Self.ToJSONObject(AOnlyUpdatedRecords, AChildRecords);
  try
    Result := {$IF DEFINED(FPC)}LJSONObject.AsJSON{$ELSE}LJSONObject.ToString{$ENDIF};
  finally
    LJSONObject.Free;
  end;
end;

function TDataSetSerializeHelper.ToJSONArrayString(const AOnlyUpdatedRecords: Boolean = False; const AChildRecords: Boolean = True): string;
var
  LJSONArray: TJSONArray;
begin
  LJSONArray := Self.ToJSONArray(AOnlyUpdatedRecords, AChildRecords);
  try
    Result := {$IF DEFINED(FPC)}LJSONArray.AsJSON{$ELSE}LJSONArray.ToString{$ENDIF};
  finally
    LJSONArray.Free;
  end;
end;

function TDataSetSerializeHelper.SaveStructure: TJSONArray;
var
  LDataSetSerialize: TDataSetSerialize;
begin
  LDataSetSerialize := TDataSetSerialize.Create(Self);
  try
    Result := LDataSetSerialize.SaveStructure;
  finally
    LDataSetSerialize.Free;
  end;
end;

function TDataSetSerializeHelper.ValidateJSON(const AJSONObject: TJSONObject; const ALang: TLanguageType = enUS; const AOwns: Boolean = True): TJSONArray;
var
  LJSONSerialize: TJSONSerialize;
begin
  LJSONSerialize := TJSONSerialize.Create(AJSONObject, AOwns);
  try
    Result := LJSONSerialize.Validate(Self, ALang);
  finally
    LJSONSerialize.Free;
  end;
end;

procedure TDataSetSerializeHelper.LoadFromJSON(const AJSONArray: TJSONArray; const AOwns: Boolean = True);
var
  LJSONSerialize: TJSONSerialize;
begin
  LJSONSerialize := TJSONSerialize.Create(AJSONArray, AOwns);
  try
    LJSONSerialize.ToDataSet(Self);
  finally
    LJSONSerialize.Free;
  end;
end;

procedure TDataSetSerializeHelper.LoadFromJSON(const AJSONObject: TJSONObject; const AOwns: Boolean = True);
var
  LJSONSerialize: TJSONSerialize;
begin
  LJSONSerialize := TJSONSerialize.Create(AJSONObject, AOwns);
  try
    LJSONSerialize.ToDataSet(Self);
  finally
    LJSONSerialize.Free;
  end;
end;

procedure TDataSetSerializeHelper.LoadStructure(const AJSONArray: TJSONArray; const AOwns: Boolean = True);
var
  LJSONSerialize: TJSONSerialize;
begin
  LJSONSerialize := TJSONSerialize.Create(AJSONArray, AOwns);
  try
    LJSONSerialize.LoadStructure(Self);
  finally
    LJSONSerialize.Free;
  end;
end;

procedure TDataSetSerializeHelper.MergeFromJSONObject(const AJSONObject: TJSONObject; const AOwns: Boolean = True);
var
  LJSONSerialize: TJSONSerialize;
begin
  LJSONSerialize := TJSONSerialize.Create(AJSONObject, AOwns);
  try
    LJSONSerialize.Merge(Self);
  finally
    LJSONSerialize.Free;
  end;
end;

function TDataSetSerializeHelper.ValidateJSON(const AJSONString: string; const ALang: TLanguageType): TJSONArray;
begin
  if Trim(AJSONString).StartsWith('{') then
    Result := ValidateJSON({$IF DEFINED(FPC)}GetJSON(AJSONString){$ELSE}TJSONObject.ParseJSONValue(TEncoding.UTF8.GetBytes(AJSONString), 0){$ENDIF} as TJSONObject, ALang)
  else
    Result := TJSONArray.Create();
end;

procedure TDataSetSerializeHelper.LoadFromJSON(const AJSONString: string);
begin
  if Trim(AJSONString).StartsWith('{') then
    LoadFromJSON({$IF DEFINED(FPC)}GetJSON(AJSONString){$ELSE}TJSONObject.ParseJSONValue(TEncoding.UTF8.GetBytes(AJSONString), 0){$ENDIF} as TJSONObject)
  else if Trim(AJSONString).StartsWith('[') then
    LoadFromJSON({$IF DEFINED(FPC)}GetJSON(AJSONString){$ELSE}TJSONObject.ParseJSONValue(TEncoding.UTF8.GetBytes(AJSONString), 0){$ENDIF} as TJSONArray);
end;

procedure TDataSetSerializeHelper.LoadStructure(const AJSONString: string);
begin
  if Trim(AJSONString).StartsWith('[') then
    LoadStructure({$IF DEFINED(FPC)}GetJSON(AJSONString){$ELSE}TJSONObject.ParseJSONValue(TEncoding.UTF8.GetBytes(AJSONString), 0){$ENDIF} as TJSONArray);
end;

procedure TDataSetSerializeHelper.MergeFromJSONObject(const AJSONString: string);
begin
  if Trim(AJSONString).StartsWith('{') then
    MergeFromJSONObject({$IF DEFINED(FPC)}GetJSON(AJSONString){$ELSE}TJSONObject.ParseJSONValue(TEncoding.UTF8.GetBytes(AJSONString), 0){$ENDIF} as TJSONObject)
end;

end.

Numerical conversion issue

Hi Vinicius,

I found a serious problem when converting the numerical numbers, e.g. from German locale.
There is set "," comma as separator by default, while I assume JSON only allows "." dot.
In US/ES this is maybe OK, but in many locales this will break into an exception.

Your Example Dataset.Serialize samples\delphi\basic\basic.dproj shows the issue when testing "Load empty dataset".

See following workaround below.
Use the already existing FormatSetting, which is defined for datetime right now only.

`unit DataSet.Serialize.Import;

...

procedure TJSONSerialize.JSONObjectToDataSet(const AJSONObject: TJSONObject; const ADataSet: TDataSet; const ADetail: Boolean);

...

          TFieldType.ftFloat, TFieldType.ftFMTBcd, TFieldType.ftBCD{$IF NOT DEFINED(FPC)}, TFieldType.ftSingle{$ENDIF}:
          begin //S4: 22.09.21 Added Decimal enforcement
            LFormatSettings.DecimalSeparator := '.'; //S4: Added
            LField.AsFloat := StrToFloat(LJSONValue.Value, LFormatSettings ); // S4: Added LFormatSettings
          end;
`

moreover, to avoid setting the DecimalSeparator every time, I would propose an optimization to do this only once,
at the head of the function.

Optimized, to set the FormatSettings only once, for Float and DateTime.
As compromize only set DecimalSeparator once, as below.

`procedure TJSONSerialize.JSONObjectToDataSet(const AJSONObject: TJSONObject; const ADataSet: TDataSet; const ADetail: Boolean);

...

begin
  if (not Assigned(AJSONObject)) or (not Assigned(ADataSet)) or (AJSONObject.Count = 0) then
    Exit;

  //S4: Optimized
  LFormatSettings.DecimalSeparator := '.';

...

          TFieldType.ftFloat, TFieldType.ftFMTBcd, TFieldType.ftBCD{$IF NOT DEFINED(FPC)}, TFieldType.ftSingle{$ENDIF}:
          begin //S4: 22.09.21 Added Decimal enforcement
//S4: Optimized            LFormatSettings.DecimalSeparator := '.'; //S4: Added
            LField.AsFloat := StrToFloat(LJSONValue.Value, LFormatSettings ); // S4: Added LFormatSettings
          end;
          TFieldType.ftString, TFieldType.ftWideString, TFieldType.ftMemo, TFieldType.ftWideMemo, TFieldType.ftGuid, TFieldType.ftFixedChar, TFieldType.ftFixedWideChar:
            LField.AsString := LJSONValue.Value;
          TFieldType.ftDate:
             LField.AsDateTime := DateOf(ISO8601ToDate(LJSONValue.Value, TDataSetSerializeConfig.GetInstance.DateInputIsUTC));
          TFieldType.ftTimeStamp, TFieldType.ftDateTime:
             LField.AsDateTime := ISO8601ToDate(LJSONValue.Value, TDataSetSerializeConfig.GetInstance.DateInputIsUTC);
          TFieldType.ftTime:
          begin
             LFormatSettings.TimeSeparator := ':';
//S4: Optimized             LFormatSettings.DecimalSeparator := '.';
             LFormatSettings.ShortTimeFormat := 'hh:mm:ss.zzz';
             LField.AsDateTime := StrToTime(LJSONValue.Value, LFormatSettings);
          end;
`


Upper and lower case

When the JSON is lowercase and the field name in the DataSet is uppercase, it's not loaded.

{ "name_field": "Sample name" }

Field name: qrySampleNAME_FIELD.AsString;

To work you have to pass JSON like this:

{ "NAME_FIELD": "Sample name" }

Duvida sobre Json Array no objeto

Suponhamos que tenho o JSON abaixo, como seria a serialização/deserialização para um dataset?

{ "id": 1, "descricao": "teste", "filhos": [ 1, 2, 3, 4] }

Teria um memTable com os fields:
id: TIntegerField
descricao: TStringField
filhos: ??????

Ele não seria um DataSetField pois não é um objeto, eu tenho somente uma lista de inteiros.

Alguma sugestão de como utilizar?

Grato.

How can I get the inserted, edited and deleted records of a dataset that is not a child dataset?

Hi,

I want to get only the inserted, edited and deleted records of the dataset (with CachedUpdates = True).
The dataset does not have child datasets.

I am using a FDMemTable to get the changed records: inserted, edited and deleted, of the dataset with the following instructions:
var
booChildRecords,
booOnlyUpdatedRecords : Boolean;
jsonChangeViews : TJSONValue;

begin
{ parMTChangeViews : TFDMemTable that receives the FDMemTable (with CachedUpdates = True), that contains
all the records inserted, updated and deleted.
It is not a child dataset.}

booChildRecords := False;
booOnlyUpdatedRecords := True;
jsonChangeViews := parMTChangeViews.ToJSONArray(booOnlyUpdatedRecords, booChildRecords);

end;

The above-instruction only gets the inserted, edited and deleted records of a child dataset?
How can I get the changed records of a dataset that is not a child dataset?

datetime field does not show time

I'm serializing this json and the datetime fields don't show the time.

{
"id": "101178",
"dataHoraInicial": "2021-06-01 07:01:47",
"DataHoraFinal": "2021-06-01 07:31:11"
}

config

TDataSetSerializeConfig.GetInstance.DataSetPrefix := ['mt', 'qry'];
TDataSetSerializeConfig.GetInstance.Export.FormatCurrency := '0.00##';
TDataSetSerializeConfig.GetInstance.Export.FormatDate := 'YYYY-MM-DD';
TDataSetSerializeConfig.GetInstance.Export.FormatDateTime := 'yyyy-mm-dd hh:mm:ss';
TDataSetSerializeConfig.GetInstance.Export.ExportNullValues := True;
TDataSetSerializeConfig.GetInstance.Export.ExportEmptyDataSet := True;
TDataSetSerializeConfig.GetInstance.CaseNameDefinition := cndNone;
TDataSetSerializeConfig.GetInstance.Export.ExportChildDataSetAsJsonObject := False;
TDataSetSerializeConfig.GetInstance.DateInputIsUTC := True;

##prints

print_data
print_fieldFDMemTable

Notify correction information for dataset-serialize

I have tried downloading and testing. dataset-serialize
Sample compilation Thai results displayed as ????
So I edited the file. "..\ src\helpers\DataSet.Serialize.pas"
By editing the order “TEncoding.ASCII.GetBytes” is “TEncoding.UTF8.GetBytes” instead. The results displayed in Thai are correct.

Thank you for good tools.

Formatação Data e Hora

A classe não publica as propriedades "FormatTime" e "FormatDateTime"

segue modificações necessárias:

-- Config.pas

TDataSetSerializeConfigExport = class
private
FExportNullValues: Boolean;
FExportNullAsEmptyString: Boolean;
FExportOnlyFieldsVisible: Boolean;
FExportEmptyDataSet: Boolean;
FFormatCurrency: string;
FFormatDate: string;
FFormatTime: string;
FFormatDateTime: string;

FExportChildDataSetAsJsonObject: Boolean;
public
constructor Create;
property FormatTime: string read FFormatTime write FFormatTime;
property FormatDateTime: string read FFormatDateTime write FFormatDateTime;

property FormatDate: string read FFormatDate write FFormatDate;
property FormatCurrency: string read FFormatCurrency write FFormatCurrency;
property ExportOnlyFieldsVisible: Boolean read FExportOnlyFieldsVisible write FExportOnlyFieldsVisible;
property ExportNullValues: Boolean read FExportNullValues write FExportNullValues;
property ExportNullAsEmptyString: Boolean read FExportNullAsEmptyString write FExportNullAsEmptyString;
property ExportEmptyDataSet: Boolean read FExportEmptyDataSet write FExportEmptyDataSet;
property ExportChildDataSetAsJsonObject: Boolean read FExportChildDataSetAsJsonObject write FExportChildDataSetAsJsonObject;
end;

constructor TDataSetSerializeConfigExport.Create;
begin
FExportNullValues := True;
FExportNullAsEmptyString:= False;
FExportOnlyFieldsVisible := True;
ExportEmptyDataSet := False;
FFormatCurrency := EmptyStr;
FFormatTime := 'hh:nn:ss.zzz';
FFormatDateTime := 'yyyy-mm-dd hh:nn:ss.zzz';

FFormatDate := 'YYYY-MM-DD';
FExportChildDataSetAsJsonObject := False;
end;

-- Export.pas

  TFieldType.ftDateTime:
       Result.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(LKey, TJSONString.Create(FormatDateTime(TDataSetSerializeConfig.GetInstance.Export.FormatDateTime, LField.AsDateTime)));
   TFieldType.ftTimeStamp:
       Result.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(LKey, TJSONString.Create(DateToISO8601(LField.AsDateTime, TDataSetSerializeConfig.GetInstance.DateInputIsUTC)));
   TFieldType.ftTime:
       Result.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(LKey, TJSONString.Create(FormatDateTime(TDataSetSerializeConfig.GetInstance.Export.FormatTime, LField.AsDateTime)));
   TFieldType.ftDate:
       Result.{$IF DEFINED(FPC)}Add{$ELSE}AddPair{$ENDIF}(LKey, TJSONString.Create(FormatDateTime(TDataSetSerializeConfig.GetInstance.Export.FormatDate, LField.AsDateTime)));

_ removed from keys

Hi.

When I select a column employee_number from a table it will show like employeeNumber in the json.
How can a make it stay the way it is in the table?

any reason for enforcing 0 field count before importing from JSON to DataSet?

i have a complex filter that cannot be achieved via standard SQLite or Filter event.
For that, a separate Boolean FieldDef is added prior to loading from JSON.

However when doing so (and in accordance to the docs), the JSON fields are not created.
commenting out the line below, works.

//    if ((ADataSet.FieldDefs.Count = 0) and (ADataSet.FieldCount = 0)) then
      LoadFieldsFromJSON(ADataSet, AJSONObject);

Could this be, maybe an option in the config, e.g. AllowExistingFields, defaulted to False to protect unknowing users, and yet allow those who want to proceed anyway, to do so?

Campos do Tipo ftMemo

Boa tarde,
Tenho um campo no meu memtable que e do tipo ftMemo, quanto uso todos os componentes REST do delphi ele popula esse tampo agora quando uso o dataset-serialize este campo não e populado... tem algo especifico que tenho que fazer?

Overload LoadFromJSON

In the Helper, create an overload of the LoadFromJSON method and remove LoadFromJSONObject and LoadFromJSONArray.

Error during load structure with string field size 0

Save structure on Dataset1 produces certain string fields with size = 0 (not always, just some)
When I restore the Structure on Dataset2 occur the error
"Size not defined for field <INSC_MUNICIPAL>."

pt-br
Ao salvar a estrutura do Dataset1 algums campos String ficam com tamanho 0
Quando eu restauro esta mesma estrutura no Dataset2 ocorre o erro "Tamanho não definido para o campo <INSC_MUNICIPAL>"

[{"alignment":"taLeftJustify","fieldName":"INSC_ESTADUAL","displayLabel":"INSC_ESTADUAL","dataType":"ftString","size":15,"key":false,"origin":"INSCRICAOCLIENTE","required":false,"visible":true,"readOnly":false,"autoGenerateValue":"arNone"},{"alignment":"taLeftJustify","fieldName":"INSC_MUNICIPAL","displayLabel":"INSC_MUNICIPAL","dataType":"ftString","size":0,"key":false,"origin":"INSC_MUNICIPAL","required":false,"visible":true,"readOnly":true,"autoGenerateValue":"arDefault"},{"alignment":"taLeftJustify","fieldName":"SUFRAMA","displayLabel":"SUFRAMA","dataType":"ftString","size":0,"key":false,"origin":"SUFRAMA","required":false,"visible":true,"readOnly":true,"autoGenerateValue":"arDefault"},{"alignment":"taLeftJustify","fieldName":"TELEFONE","displayLabel":"TELEFONE","dataType":"ftString","size":15,"key":false,"origin":"TELEFONECLIENTE","required":false,"visible":true,"readOnly":false,"autoGenerateValue":"arNone"}]

ReportMemory

Estou usando a biblioteca para converter um query para json como estou debugando o ReportMemoryLeaksOnShutdown esta ativo porem esta dando vazamento de memoria

JSONObject, TJSONString, TJSONPair

Posso está fazendo algo errado? Grato

Format of floating point numbers.

Hi. How do you format float or numbers.
Where I select data from a table in postgresql the number fields will show like thi 1.3640000000000000E+003
But the number in the database is 1346

Duvidas - LowerCamelCase - JsonArray master-detail

Ola.
Primeiramente parabens a todos os envolvidos pelo trabalho. exelente ferramenta!

Estou gerando um JSONArray master - detail - detail que seriam grupo->subgrupos->adicionais
a primeria questao e que cuando marco o LowerCamelCase:= true, o detail fica por exemplo dessa forma ->"sUBGRUPOS", "aDICIONAIS"

outra questao e quando por exemplo nao existem adicionais na detail teria uma forma de gerar
ela vazia?. exemplo -> "Adicionais": [] ;

os campos currency por exemplo "valor": "0,00" estao vindo como string no json, tem alguma forma de colocar eles como double?

no mastes estou fazendo da seguinte forma
LJSONArray := GRUPOS.ToJSONArray(False,True);

Obrigado.

segue uma amostra do json gerado.

{
"idGrupo": 9,
"descricao": "ESFIHAS",
"sUBGRUPOS": [
{
"id": 11,
"idSubgrupo": 11,
"descricao": "ESFIHAS",
"imagem": "",
"agrupa": "N",
"nAgrupa": null,
"idGrupo": 9,
"nFatias": 0,
"valor": "0,00"
}
]
},
{
"idGrupo": 6,
"descricao": "LANCHES",
"sUBGRUPOS": [
{
"id": 8,
"idSubgrupo": 8,
"descricao": "LANCHES",
"imagem": "",
"agrupa": "N",
"nAgrupa": null,
"idGrupo": 6,
"nFatias": 0,
"valor": "0,00",
"aDICIONAIS": [
{
"id": 18,
"descricao": "ADICIONAIS",
"valor": "2,00",
"idSubgrupo": 8
},
{
"id": 5,
"descricao": "EGG",
"valor": "1,50",
"idSubgrupo": 8
}
]
}
]
},

Memory Leak

Hi.

The library seems to have a major memory leak.

When I start the program this is the memory usage

image

After making a couple of calls to it where the library will convert the dataset into json the memory will grow a lot and not drop down again.

image

Export json keys in lowercase

Today is exporting according to what is put in the DataSet. By default and web convention, you must export the keys in lowercase.

Wrong:

{ "PAIS": "Brasil", "SIGLA": "BRL", "ESTADOS": [ { "NOME": "Sao Paulo", "SIGLA": "SP", "CIDADES": [ { "NOME": "Fernandopolis", "CEP": "15600-000" }, { "NOME": "Votuporanga", "CEP": "12600-100" }, { "NOME": "Jales", "CEP": "14859-000" }, { "NOME": "Sao Jose do Rio Preto", "CEP": "18450-450" }, { "NOME": "Balsamo", "CEP": "15140-000" } ] }, { "NOME": "Parana", "SIGLA": "PR", "CIDADES": [ { "NOME": "Maringa", "CEP": "87050-000" }, { "NOME": "Londrina", "CEP": "86010-190" } ] } ] }

Right:

{ "pais": "Brasil", "sigla": "BRL", "estados": [ { "nome": "Sao Paulo", "sigla": "SP", "cidades": [ { "nome": "Fernandopolis", "cep": "15600-000" }, { "nome": "Votuporanga", "cep": "12600-100" }, { "nome": "Jales", "cep": "14859-000" }, { "nome": "Sao Jose do Rio Preto", "cep": "18450-450" }, { "nome": "Balsamo", "cep": "15140-000" } ] }, { "nome": "Parana", "sigla": "PR", "cidades": [ { "nome": "Maringa", "cep": "87050-000" }, { "nome": "Londrina", "cep": "86010-190" } ] } ] }

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.