viniciussanchez / dataset-serialize Goto Github PK
View Code? Open in Web Editor NEWJSON to DataSet and DataSet to JSON converter for Delphi and Lazarus (FPC)
License: MIT License
JSON to DataSet and DataSet to JSON converter for Delphi and Lazarus (FPC)
License: MIT License
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;
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 ?
Export and load readonly property from dataset
Create a configuration class
Hi.
Why does the JSON object start with [ and end with ] ?
It breaks the JSON parsers.
I I convert a result set from POSTGRESQL I will get a string like this.
[{"week_total" : 1.6719999999999999E+001}]
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));
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.
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
fuction ValidateJSON accept JSON in string format
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 ??
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.
Implement lazarus compatibility (free pascal)
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
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
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.
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.
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 ;-)
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.
Export and load AutoGenerateValue property from dataset
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"
}
]
O FormatCurrency é ignorado para campos DOUBLE (12,4) no MySQL.
Dentro do Delphi o campo é identificado como TFieldType.ftSingle
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.
Add Sample to Export Nested DataSet
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
}
]
}
}
Implement TDD
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));
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;
`
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
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.
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.
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;
`
Change the parameter for new json formatting possibilities
Today has an example using nested JSON with memtable. Implement example using query with database access.
Modify the readme.md file with new samples.
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" }
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.
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?
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"
}
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
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.
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)));
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?
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?
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?
Unicode characters appears incorrectly. When a string contains Non-Latin characters, it leaves question marks (???).
Does this handle fields of type BLOB?
Save the dataset before loading parent dataset details when working with nested JSON.
In the Helper, create an overload of the LoadFromJSON method and remove LoadFromJSONObject and LoadFromJSONArray.
Create configuration samples lazarus equals delphi samples
Create master detail samples for lazarus equals delphi samples
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"}]
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
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
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
}
]
}
]
},
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" } ] } ] }
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.