delphimvcframework/sources/ObjectsMappers.pas

2351 lines
72 KiB
ObjectPascal
Raw Normal View History

2013-10-30 00:48:23 +01:00
{ *******************************************************************************
Copyright 2010-2013 Daniele Teti
2013-10-30 00:48:23 +01:00
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
******************************************************************************** }
unit ObjectsMappers;
interface
uses
System.RTTI,
System.IOUtils,
DBXPLatform,
DB,
Generics.Collections,
2014-04-16 22:52:25 +02:00
{$IF Defined(VER270)}
System.JSON,
{$ELSE}
Data.DBXJSON,
Data.SqlExpr,
2014-04-22 00:20:00 +02:00
DBXCommon,
2014-04-16 22:52:25 +02:00
{$IFEND}
2014-04-22 00:20:00 +02:00
{$IF Defined(VER260) or Defined(VER270)}
FireDAC.Comp.Client, FireDAC.Stan.Param,
{$IFEND}
DuckListU;
2013-10-30 00:48:23 +01:00
type
2014-04-22 00:20:00 +02:00
TJSONObjectActionProc = reference to procedure(const AJSONObject: TJSONObject);
2013-10-30 00:48:23 +01:00
Mapper = class
strict private
class var ctx: TRTTIContext;
private
2014-04-16 22:52:25 +02:00
{$IF not Defined(VER270)}
2014-04-22 00:20:00 +02:00
class function InternalExecuteSQLQuery(AQuery: TSQLQuery; AObject: TObject; WithResult: boolean): Int64;
2014-04-16 22:52:25 +02:00
{$IFEND}
2014-04-22 00:20:00 +02:00
{$IF Defined(VER260) or Defined(VER270)}
class function InternalExecuteFDQuery(AQuery: TFDQuery; AObject: TObject; WithResult: boolean): Int64;
2014-02-24 10:20:34 +01:00
{$IFEND}
2014-04-22 00:20:00 +02:00
class function GetKeyName(const ARttiField: TRttiField; AType: TRttiType): string; overload;
class function GetKeyName(const ARttiProp: TRttiProperty; AType: TRttiType): string; overload;
class procedure InternalJSONObjectToObject(ctx: TRTTIContext; AJSONObject: TJSONObject;
AObject: TObject); static;
2013-10-30 00:48:23 +01:00
public
class function HasAttribute<T: class>(ARTTIMember: TRttiNamedObject): boolean; overload;
class function HasAttribute<T: class>(ARTTIMember: TRttiNamedObject; out AAttribute: T): boolean; overload;
2013-10-30 00:48:23 +01:00
///
/// Do not restore nested classes
///
2014-04-22 00:20:00 +02:00
class function JSONObjectToObject<T: constructor, class>(AJSONObject: TJSONObject): T; overload; static;
class function JSONObjectStringToObject<T: constructor, class>(const AJSONObjectString: string): T;
2014-04-22 00:20:00 +02:00
class function JSONObjectToObject(Clazz: TClass; AJSONObject: TJSONObject): TObject; overload; static;
class function JSONObjectToObject(ClazzName: string; AJSONObject: TJSONObject): TObject; overload; static;
class function JSONObjectToObjectFields<T: constructor, class>(AJSONObject: TJSONObject): T; static;
class procedure ObjectToDataSet(Obj: TObject; Field: TField; var Value: Variant); static;
2013-10-30 00:48:23 +01:00
class procedure DataSetToObject(ADataSet: TDataSet; AObject: TObject);
class function ObjectToJSONObject(AObject: TObject; AIgnoredProperties: array of string)
: TJSONObject; overload;
2014-04-22 00:20:00 +02:00
class function ObjectToJSONObjectFields(AObject: TObject; AIgnoredProperties: array of string)
: TJSONObject; overload;
2013-10-30 00:48:23 +01:00
class function ObjectToJSONObject(AObject: TObject): TJSONObject; overload;
class function ObjectToJSONObjectString(AObject: TObject): string;
2013-10-30 00:48:23 +01:00
class function ObjectToJSONArray(AObject: TObject): TJSONArray;
class function JSONArrayToObjectList<T: class, constructor>(AJSONArray: TJSONArray;
AInstanceOwner: boolean = True;
2014-04-16 22:52:25 +02:00
AOwnsChildObjects: boolean = True): TObjectList<T>; overload;
2014-04-22 00:20:00 +02:00
class procedure JSONArrayToObjectList<T: class, constructor>(AList: TObjectList<T>; AJSONArray: TJSONArray;
AInstanceOwner: boolean = True; AOwnsChildObjects: boolean = True); overload;
2014-04-16 22:52:25 +02:00
{$IF not Defined(VER270)}
2013-10-30 00:48:23 +01:00
class procedure ReaderToObject(AReader: TDBXReader; AObject: TObject);
class procedure ReaderToObjectList<T: class, constructor>(AReader: TDBXReader;
AObjectList: TObjectList<T>);
2014-04-22 00:20:00 +02:00
class procedure ReaderToJSONObject(AReader: TDBXReader; AJSONObject: TJSONObject;
AReaderInstanceOwner: boolean = True);
2014-04-16 22:52:25 +02:00
{$ENDIF}
2014-04-22 00:20:00 +02:00
class procedure DataSetToJSONObject(ADataSet: TDataSet; AJSONObject: TJSONObject;
ADataSetInstanceOwner: boolean = True);
class procedure JSONObjectToDataSet(AJSONObject: TJSONObject; ADataSet: TDataSet;
AJSONObjectInstanceOwner: boolean = True); overload;
class procedure JSONObjectToDataSet(AJSONObject: TJSONObject; ADataSet: TDataSet;
AIgnoredFields: TArray<string>;
2014-04-16 22:52:25 +02:00
AJSONObjectInstanceOwner: boolean = True); overload;
2014-04-22 00:20:00 +02:00
class procedure DataSetToObjectList<T: class, constructor>(ADataSet: TDataSet; AObjectList: TObjectList<T>;
2014-04-16 22:52:25 +02:00
ACloseDataSetAfterScroll: boolean = True);
2014-04-22 00:20:00 +02:00
class function DataSetToJSONArrayOf<T: class, constructor>(ADataSet: TDataSet): TJSONArray;
2014-04-16 22:52:25 +02:00
{$IF not Defined(VER270)}
2014-04-22 00:20:00 +02:00
class procedure ReaderToList<T: class, constructor>(AReader: TDBXReader; AList: IWrappedList);
class procedure ReaderToJSONArray(AReader: TDBXReader; AJSONArray: TJSONArray;
AReaderInstanceOwner: boolean = True);
2014-04-16 22:52:25 +02:00
{$ENDIF}
2014-04-22 00:20:00 +02:00
class procedure DataSetToJSONArray(ADataSet: TDataSet; AJSONArray: TJSONArray;
ADataSetInstanceOwner: boolean = True);
class procedure JSONArrayToDataSet(AJSONArray: TJSONArray; ADataSet: TDataSet;
AJSONArrayInstanceOwner: boolean = True); overload;
class procedure JSONArrayToDataSet(AJSONArray: TJSONArray; ADataSet: TDataSet;
AIgnoredFields: TArray<string>;
2014-03-24 13:17:30 +01:00
AJSONArrayInstanceOwner: boolean = True); overload;
2013-11-11 01:11:09 +01:00
// class procedure DataSetRowToXML(ADataSet: TDataSet; Row: IXMLNode;
// ADataSetInstanceOwner: boolean = True);
// class procedure DataSetToXML(ADataSet: TDataSet; XMLDocument: String;
// ADataSetInstanceOwner: boolean = True);
2014-04-22 00:20:00 +02:00
class function ObjectListToJSONArray<T: class>(AList: TObjectList<T>; AOwnsInstance: boolean = false;
AForEach: TJSONObjectActionProc = nil): TJSONArray;
class function ObjectListToJSONArrayString<T: class>(AList: TObjectList<T>;
AOwnsInstance: boolean = false): string;
2014-04-22 00:20:00 +02:00
class function ObjectListToJSONArrayOfJSONArray<T: class, constructor>(AList: TObjectList<T>): TJSONArray;
class function GetProperty(Obj: TObject; const PropertyName: string): TValue; static;
2014-04-16 22:52:25 +02:00
{$IF not Defined(VER270)}
2014-04-22 00:20:00 +02:00
class function ExecuteSQLQueryNoResult(AQuery: TSQLQuery; AObject: TObject): Int64;
2013-10-30 00:48:23 +01:00
class procedure ExecuteSQLQuery(AQuery: TSQLQuery; AObject: TObject = nil);
class function ExecuteSQLQueryAsObjectList<T: class, constructor>(AQuery: TSQLQuery;
AObject: TObject = nil)
2014-04-22 00:20:00 +02:00
: TObjectList<T>;
2014-04-16 22:52:25 +02:00
{$ENDIF}
2014-02-24 10:20:34 +01:00
{ FIREDAC RELATED METHODS }
2014-04-22 00:20:00 +02:00
{$IF Defined(VER260) or Defined(VER270)}
class function ExecuteFDQueryNoResult(AQuery: TFDQuery; AObject: TObject): Int64;
2014-02-24 10:20:34 +01:00
class procedure ExecuteFDQuery(AQuery: TFDQuery; AObject: TObject);
2014-04-22 00:20:00 +02:00
class procedure ObjectToFDParameters(AFDParams: TFDParams; AObject: TObject; AParamPrefix: string = '');
2014-04-16 22:52:25 +02:00
{$IFEND}
{$IF not Defined(VER270)}
2014-04-22 00:20:00 +02:00
class function CreateQuery(AConnection: TSQLConnection; ASQL: string): TSQLQuery;
2014-02-24 10:20:34 +01:00
{$IFEND}
2013-10-30 00:48:23 +01:00
// SAFE TJSONObject getter
2014-04-22 00:20:00 +02:00
class function GetPair(JSONObject: TJSONObject; PropertyName: string): TJSONPair;
class function GetStringDef(JSONObject: TJSONObject; PropertyName: string;
DefaultValue: string = ''): string;
class function GetNumberDef(JSONObject: TJSONObject; PropertyName: string; DefaultValue: Extended = 0)
: Extended;
2014-04-22 00:20:00 +02:00
class function GetJSONObj(JSONObject: TJSONObject; PropertyName: string): TJSONObject;
class function GetJSONArray(JSONObject: TJSONObject; PropertyName: string): TJSONArray;
class function GetIntegerDef(JSONObject: TJSONObject; PropertyName: string;
DefaultValue: Integer = 0): Integer;
2014-04-22 00:20:00 +02:00
class function GetInt64Def(JSONObject: TJSONObject; PropertyName: string; DefaultValue: Int64 = 0): Int64;
class function GetBooleanDef(JSONObject: TJSONObject; PropertyName: string;
DefaultValue: boolean = false): boolean;
2014-04-22 00:20:00 +02:00
class function PropertyExists(JSONObject: TJSONObject; PropertyName: string): boolean;
2013-10-30 00:48:23 +01:00
end;
TDataSetHelper = class helper for TDataSet
public
function AsJSONArray: TJSONArray;
function AsJSONArrayString: string;
2014-03-13 01:01:27 +01:00
function AsJSONObject(AReturnNilIfEOF: boolean = false): TJSONObject;
function AsJSONObjectString(AReturnEmptyStringIfEOF: boolean = false): string;
2014-03-24 13:17:30 +01:00
procedure LoadFromJSONObject(AJSONObject: TJSONObject); overload;
procedure LoadFromJSONObject(AJSONObject: TJSONObject; AIgnoredFields: TArray<string>); overload;
2014-03-24 13:17:30 +01:00
procedure LoadFromJSONArray(AJSONArray: TJSONArray); overload;
procedure LoadFromJSONArray(AJSONArray: TJSONArray; AIgnoredFields: TArray<string>); overload;
procedure LoadFromJSONObjectString(AJSONObjectString: string); overload;
procedure LoadFromJSONObjectString(AJSONObjectString: string; AIgnoredFields: TArray<string>); overload;
procedure AppendFromJSONArrayString(AJSONArrayString: string); overload;
procedure AppendFromJSONArrayString(AJSONArrayString: string; AIgnoredFields: TArray<string>); overload;
2014-04-22 00:20:00 +02:00
function AsObjectList<T: class, constructor>(CloseAfterScroll: boolean = false): TObjectList<T>;
function AsObject<T: class, constructor>(CloseAfterScroll: boolean = false): T;
end;
2013-10-30 00:48:23 +01:00
MapperTransientAttribute = class(TCustomAttribute)
end;
DoNotSerializeAttribute = class(TCustomAttribute)
end;
2013-10-30 00:48:23 +01:00
MapperItemsClassType = class(TCustomAttribute)
private
FValue: TClass;
procedure SetValue(const Value: TClass);
2014-03-07 23:16:33 +01:00
public
2013-10-30 00:48:23 +01:00
constructor Create(Value: TClass);
property Value: TClass read FValue write SetValue;
end;
MapperListOf = MapperItemsClassType; // just to be more similar to DORM
2013-10-30 00:48:23 +01:00
TJSONNameCase = (JSONNameUpperCase, JSONNameLowerCase);
HideInGrids = class(TCustomAttribute)
end;
StringValueAttribute = class abstract(TCustomAttribute)
private
FValue: string;
procedure SetValue(const Value: string);
public
constructor Create(Value: string);
property Value: string read FValue write SetValue;
end;
FormatFloatValue = class(StringValueAttribute)
end;
FormatDateTimeValue = class(StringValueAttribute)
end;
2014-04-10 13:56:23 +02:00
MapperSerializeAsString = class(TCustomAttribute)
end;
2013-10-30 00:48:23 +01:00
MapperJSONNaming = class(TCustomAttribute)
private
FJSONKeyCase: TJSONNameCase;
function GetKeyCase: TJSONNameCase;
public
constructor Create(JSONKeyCase: TJSONNameCase);
property KeyCase: TJSONNameCase read GetKeyCase;
end;
MapperJSONSer = class(TCustomAttribute)
private
FName: string;
function GetName: string;
public
constructor Create(AName: string);
property name: string read GetName;
end;
MapperColumnAttribute = class(TCustomAttribute)
private
FFieldName: string;
2013-11-10 01:04:17 +01:00
FIsPK: boolean;
2013-10-30 00:48:23 +01:00
procedure SetFieldName(const Value: string);
procedure SetIsPK(const Value: boolean);
public
constructor Create(AFieldName: string; AIsPK: boolean = false);
property FieldName: string read FFieldName write SetFieldName;
property IsPK: boolean read FIsPK write SetIsPK;
end;
TGridColumnAlign = (caLeft, caCenter, caRight);
GridColumnProps = class(TCustomAttribute)
private
FCaption: string;
2013-11-10 01:04:17 +01:00
FAlign: TGridColumnAlign;
FWidth: Integer;
2013-10-30 00:48:23 +01:00
function GetAlignAsString: string;
public
2014-04-22 00:20:00 +02:00
constructor Create(ACaption: string; AAlign: TGridColumnAlign = caCenter; AWidth: Integer = -1);
2013-10-30 00:48:23 +01:00
property Caption: string read FCaption;
property Align: TGridColumnAlign read FAlign;
property AlignAsString: string read GetAlignAsString;
property Width: Integer read FWidth;
end;
function ISODateTimeToString(ADateTime: TDateTime): string;
function ISODateToString(ADate: TDateTime): string;
function ISOTimeToString(ATime: TTime): string;
function ISOStrToDateTime(DateTimeAsString: string): TDateTime;
function ISOStrToDate(DateAsString: string): TDate;
function ISOStrToTime(TimeAsString: string): TTime;
// function ISODateToStr(const ADate: TDate): String;
//
// function ISOTimeToStr(const ATime: TTime): String;
implementation
uses
TypInfo,
SysUtils,
FmtBcd,
Math,
SqlTimSt,
DateUtils,
Classes,
2013-11-10 01:04:17 +01:00
RTTIUtilsU,
Soap.EncdDecd,
2013-11-10 01:04:17 +01:00
Xml.adomxmldom;
2013-10-30 00:48:23 +01:00
{ Mapper }
function ContainsFieldName(const FieldName: string; var FieldsArray: TArray<string>): boolean;
2014-03-24 13:17:30 +01:00
var
I: Integer;
begin
for I := 0 to Length(FieldsArray) - 1 do
begin
if SameText(FieldsArray[I], FieldName) then
Exit(True);
end;
Result := false;
end;
2013-10-30 00:48:23 +01:00
function ISOTimeToString(ATime: TTime): string;
var
fs: TFormatSettings;
begin
fs.TimeSeparator := ':';
Result := FormatDateTime('hh:nn:ss', ATime, fs);
end;
function ISODateToString(ADate: TDateTime): string;
begin
Result := FormatDateTime('YYYY-MM-DD', ADate);
end;
function ISODateTimeToString(ADateTime: TDateTime): string;
var
fs: TFormatSettings;
begin
fs.TimeSeparator := ':';
Result := FormatDateTime('yyyy-mm-dd hh:nn:ss', ADateTime, fs);
end;
function ISOStrToDateTime(DateTimeAsString: string): TDateTime;
begin
2014-04-22 00:20:00 +02:00
Result := EncodeDateTime(StrToInt(Copy(DateTimeAsString, 1, 4)), StrToInt(Copy(DateTimeAsString, 6, 2)),
StrToInt(Copy(DateTimeAsString, 9, 2)), StrToInt(Copy(DateTimeAsString, 12, 2)),
StrToInt(Copy(DateTimeAsString, 15, 2)), StrToInt(Copy(DateTimeAsString, 18, 2)), 0);
2013-10-30 00:48:23 +01:00
end;
function ISOStrToTime(TimeAsString: string): TTime;
begin
2014-04-22 00:20:00 +02:00
Result := EncodeTime(StrToInt(Copy(TimeAsString, 1, 2)), StrToInt(Copy(TimeAsString, 4, 2)),
StrToInt(Copy(TimeAsString, 7, 2)), 0);
2013-10-30 00:48:23 +01:00
end;
function ISOStrToDate(DateAsString: string): TDate;
begin
2014-04-22 00:20:00 +02:00
Result := EncodeDate(StrToInt(Copy(DateAsString, 1, 4)), StrToInt(Copy(DateAsString, 6, 2)),
StrToInt(Copy(DateAsString, 9, 2)));
2013-10-30 00:48:23 +01:00
// , StrToInt
// (Copy(DateAsString, 12, 2)), StrToInt(Copy(DateAsString, 15, 2)),
// StrToInt(Copy(DateAsString, 18, 2)), 0);
end;
// function ISODateToStr(const ADate: TDate): String;
// begin
// Result := FormatDateTime('YYYY-MM-DD', ADate);
// end;
//
// function ISOTimeToStr(const ATime: TTime): String;
// begin
// Result := FormatDateTime('HH:nn:ss', ATime);
// end;
2014-04-16 22:52:25 +02:00
{$IF not Defined(VER270)}
class function Mapper.InternalExecuteSQLQuery(AQuery: TSQLQuery; AObject: TObject;
WithResult: boolean): Int64;
2014-04-16 22:52:25 +02:00
var
I: Integer;
pname: string;
_rttiType: TRttiType;
obj_fields: TArray<TRttiProperty>;
obj_field: TRttiProperty;
obj_field_attr: MapperColumnAttribute;
Map: TObjectDictionary<string, TRttiProperty>;
f: TRttiProperty;
fv: TValue;
begin
Map := TObjectDictionary<string, TRttiProperty>.Create;
try
if Assigned(AObject) then
begin
_rttiType := ctx.GetType(AObject.ClassType);
obj_fields := _rttiType.GetProperties;
for obj_field in obj_fields do
begin
if HasAttribute<MapperColumnAttribute>(obj_field, obj_field_attr) then
begin
Map.Add(MapperColumnAttribute(obj_field_attr).FieldName, obj_field);
end
else
begin
Map.Add(LowerCase(obj_field.Name), obj_field);
end
end;
end;
for I := 0 to AQuery.Params.Count - 1 do
begin
pname := AQuery.Params[I].Name;
if Map.TryGetValue(pname, f) then
begin
fv := f.GetValue(AObject);
AQuery.Params[I].Value := fv.AsVariant;
end
else
begin
AQuery.Params[I].Clear;
AQuery.Params[I].DataType := ftString; // just to make dbx happy
end;
end;
Result := 0;
if WithResult then
AQuery.Open
else
Result := AQuery.ExecSQL;
finally
Map.Free;
end;
end;
class procedure Mapper.ReaderToJSONArray(AReader: TDBXReader; AJSONArray: TJSONArray;
AReaderInstanceOwner: boolean);
2014-04-16 22:52:25 +02:00
var
Obj: TJSONObject;
begin
while AReader.Next do
begin
Obj := TJSONObject.Create;
AJSONArray.AddElement(Obj);
ReaderToJSONObject(AReader, Obj, false);
end;
if AReaderInstanceOwner then
FreeAndNil(AReader);
end;
class procedure Mapper.ReaderToJSONObject(AReader: TDBXReader; AJSONObject: TJSONObject;
AReaderInstanceOwner: boolean);
2014-04-16 22:52:25 +02:00
var
I: Integer;
key: string;
dt: TDateTime;
Time: TTimeStamp;
ts: TSQLTimeStamp;
begin
for I := 0 to AReader.ColumnCount - 1 do
begin
key := LowerCase(AReader.Value[I].ValueType.Name);
case AReader.Value[I].ValueType.DataType of
TDBXDataTypes.Int16Type:
AJSONObject.AddPair(key, TJSONNumber.Create(AReader.Value[I].AsInt16));
TDBXDataTypes.Int32Type:
AJSONObject.AddPair(key, TJSONNumber.Create(AReader.Value[I].AsInt32));
TDBXDataTypes.Int64Type:
AJSONObject.AddPair(key, TJSONNumber.Create(AReader.Value[I].AsInt64));
TDBXDataTypes.DoubleType:
AJSONObject.AddPair(key, TJSONNumber.Create(AReader.Value[I].AsDouble));
TDBXDataTypes.AnsiStringType, TDBXDataTypes.WideStringType:
AJSONObject.AddPair(key, AReader.Value[I].AsString);
TDBXDataTypes.BcdType:
2014-04-22 00:20:00 +02:00
AJSONObject.AddPair(key, TJSONNumber.Create(BcdToDouble(AReader.Value[I].AsBcd)));
2014-04-16 22:52:25 +02:00
TDBXDataTypes.DateType:
begin
if not AReader.Value[I].IsNull then
begin
Time.Time := 0;
Time.date := AReader.Value[I].AsDate;
dt := TimeStampToDateTime(Time);
AJSONObject.AddPair(key, ISODateToString(dt));
end
else
AJSONObject.AddPair(key, TJSONNull.Create);
end;
TDBXDataTypes.TimeType:
begin
if not AReader.Value[I].IsNull then
begin
ts := AReader.Value[I].AsTimeStamp;
AJSONObject.AddPair(key, SQLTimeStampToStr('hh:nn:ss', ts));
end
else
AJSONObject.AddPair(key, TJSONNull.Create);
end
else
raise Exception.Create('Cannot find type');
end;
end;
if AReaderInstanceOwner then
FreeAndNil(AReader);
end;
2014-04-22 00:20:00 +02:00
class procedure Mapper.ReaderToList<T>(AReader: TDBXReader; AList: IWrappedList);
2014-04-16 22:52:25 +02:00
var
Obj: T;
begin
while AReader.Next do
begin
Obj := T.Create;
ReaderToObject(AReader, Obj);
AList.Add(Obj);
end;
AReader.Close;
end;
class procedure Mapper.ReaderToObject(AReader: TDBXReader; AObject: TObject);
var
_type: TRttiType;
_fields: TArray<TRttiProperty>;
_field: TRttiProperty;
_attribute: MapperColumnAttribute;
_dict: TDictionary<string, string>;
_keys: TDictionary<string, boolean>;
mf: MapperColumnAttribute;
field_name: string;
Value: TValue;
ts: TTimeStamp;
sqlts: TSQLTimeStamp;
begin
_dict := TDictionary<string, string>.Create();
_keys := TDictionary<string, boolean>.Create();
_type := ctx.GetType(AObject.ClassInfo);
_fields := _type.GetProperties;
for _field in _fields do
if HasAttribute<MapperColumnAttribute>(_field, _attribute) then
begin
mf := _attribute;
_dict.Add(_field.Name, mf.FieldName);
_keys.Add(_field.Name, mf.IsPK);
end
else
begin
_dict.Add(_field.Name, _field.Name);
_keys.Add(_field.Name, false);
end;
for _field in _fields do
begin
2014-04-22 00:20:00 +02:00
if (not _dict.TryGetValue(_field.Name, field_name)) or (not _field.IsWritable) or
(HasAttribute<MapperTransientAttribute>(_field)) then
2014-04-16 22:52:25 +02:00
Continue;
case _field.PropertyType.TypeKind of
tkInteger:
Value := AReader.Value[field_name].AsInt32;
tkFloat:
begin
if AReader.Value[field_name].IsNull then
Value := 0
else
begin
2014-04-22 00:20:00 +02:00
if AReader.Value[field_name].ValueType.DataType = TDBXDataTypes.DateType then
2014-04-16 22:52:25 +02:00
begin
ts.Time := 0;
ts.date := AReader.Value[field_name].AsDate;
Value := TimeStampToDateTime(ts);
end
2014-04-22 00:20:00 +02:00
else if AReader.Value[field_name].ValueType.DataType = TDBXDataTypes.DoubleType then
2014-04-16 22:52:25 +02:00
Value := AReader.Value[field_name].AsDouble
2014-04-22 00:20:00 +02:00
else if AReader.Value[field_name].ValueType.DataType = TDBXDataTypes.BcdType then
2014-04-16 22:52:25 +02:00
Value := BcdToDouble(AReader.Value[field_name].AsBcd)
2014-04-22 00:20:00 +02:00
else if AReader.Value[field_name].ValueType.DataType = TDBXDataTypes.TimeType then
2014-04-16 22:52:25 +02:00
begin
sqlts := AReader.Value[field_name].AsTimeStamp;
Value := SQLTimeStampToDateTime(sqlts);
end
else
raise Exception.Create('Unknown tkFloat Type');
end;
end;
tkString, tkUString, tkWChar, tkLString, tkWString:
begin
if AReader.Value[field_name].IsNull then
Value := ''
else
Value := AReader.Value[field_name].AsString;
end;
else
raise Exception.Create('Unknown field type for ' + field_name);
end;
_field.SetValue(AObject, Value);
end;
_dict.Free;
_keys.Free;
end;
2014-04-22 00:20:00 +02:00
class procedure Mapper.ReaderToObjectList<T>(AReader: TDBXReader; AObjectList: TObjectList<T>);
2014-04-16 22:52:25 +02:00
var
Obj: T;
begin
while AReader.Next do
begin
Obj := T.Create;
ReaderToObject(AReader, Obj);
AObjectList.Add(Obj);
end;
AReader.Close;
end;
2014-04-22 00:20:00 +02:00
class function Mapper.CreateQuery(AConnection: TSQLConnection; ASQL: string): TSQLQuery;
2013-10-30 00:48:23 +01:00
begin
Result := TSQLQuery.Create(nil);
Result.SQLConnection := AConnection;
Result.CommandText := ASQL;
end;
2014-04-16 22:52:25 +02:00
{$IFEND}
2013-10-30 00:48:23 +01:00
class procedure Mapper.DataSetToJSONArray(ADataSet: TDataSet; AJSONArray: TJSONArray;
ADataSetInstanceOwner: boolean);
2013-10-30 00:48:23 +01:00
var
Obj: TJSONObject;
begin
2014-03-24 13:17:30 +01:00
repeat
2013-10-30 00:48:23 +01:00
Obj := TJSONObject.Create;
AJSONArray.AddElement(Obj);
DataSetToJSONObject(ADataSet, Obj, false);
ADataSet.Next;
2014-03-24 13:17:30 +01:00
until ADataSet.Eof;
2013-10-30 00:48:23 +01:00
if ADataSetInstanceOwner then
FreeAndNil(ADataSet);
end;
class function Mapper.DataSetToJSONArrayOf<T>(ADataSet: TDataSet): TJSONArray;
var
list: TObjectList<T>;
begin
list := TObjectList<T>.Create;
try
Mapper.DataSetToObjectList<T>(ADataSet, list);
Result := Mapper.ObjectListToJSONArray<T>(list);
finally
list.Free;
end;
end;
2014-04-22 00:20:00 +02:00
class procedure Mapper.DataSetToJSONObject(ADataSet: TDataSet; AJSONObject: TJSONObject;
ADataSetInstanceOwner: boolean);
2013-10-30 00:48:23 +01:00
var
2013-11-10 01:04:17 +01:00
I: Integer;
key: string;
ts: TSQLTimeStamp;
MS: TMemoryStream;
SS: TStringStream;
2013-10-30 00:48:23 +01:00
begin
for I := 0 to ADataSet.FieldCount - 1 do
begin
key := LowerCase(ADataSet.Fields[I].FieldName);
2014-03-07 23:16:33 +01:00
if ADataSet.Fields[I].IsNull then
begin
AJSONObject.AddPair(key, TJSONNull.Create);
Continue;
end;
2013-10-30 00:48:23 +01:00
case ADataSet.Fields[I].DataType of
2014-04-22 00:20:00 +02:00
TFieldType.ftInteger, TFieldType.ftAutoInc, TFieldType.ftSmallint, TFieldType.ftShortint:
AJSONObject.AddPair(key, TJSONNumber.Create(ADataSet.Fields[I].AsInteger));
2013-10-30 00:48:23 +01:00
TFieldType.ftLargeint:
begin
2014-04-22 00:20:00 +02:00
AJSONObject.AddPair(key, TJSONNumber.Create(ADataSet.Fields[I].AsLargeInt));
2013-10-30 00:48:23 +01:00
end;
TFieldType.ftSingle, TFieldType.ftFloat:
2014-04-22 00:20:00 +02:00
AJSONObject.AddPair(key, TJSONNumber.Create(ADataSet.Fields[I].AsFloat));
2013-10-30 00:48:23 +01:00
ftString, ftWideString, ftMemo:
AJSONObject.AddPair(key, ADataSet.Fields[I].AsWideString);
TFieldType.ftDate:
begin
if not ADataSet.Fields[I].IsNull then
begin
2014-04-22 00:20:00 +02:00
AJSONObject.AddPair(key, ISODateToString(ADataSet.Fields[I].AsDateTime));
2013-10-30 00:48:23 +01:00
end
else
AJSONObject.AddPair(key, TJSONNull.Create);
end;
TFieldType.ftDateTime:
begin
if not ADataSet.Fields[I].IsNull then
begin
2014-04-22 00:20:00 +02:00
AJSONObject.AddPair(key, ISODateTimeToString(ADataSet.Fields[I].AsDateTime));
2013-10-30 00:48:23 +01:00
end
else
AJSONObject.AddPair(key, TJSONNull.Create);
end;
TFieldType.ftTimeStamp:
begin
if not ADataSet.Fields[I].IsNull then
begin
ts := ADataSet.Fields[I].AsSQLTimeStamp;
AJSONObject.AddPair(key, SQLTimeStampToStr('hh:nn:ss', ts));
end
else
AJSONObject.AddPair(key, TJSONNull.Create);
end;
TFieldType.ftCurrency:
begin
if not ADataSet.Fields[I].IsNull then
begin
2014-04-22 00:20:00 +02:00
AJSONObject.AddPair(key, FormatCurr('0.00##', ADataSet.Fields[I].AsCurrency));
2013-10-30 00:48:23 +01:00
end
else
AJSONObject.AddPair(key, TJSONNull.Create);
end;
TFieldType.ftFMTBcd:
begin
if not ADataSet.Fields[I].IsNull then
begin
2014-04-22 00:20:00 +02:00
AJSONObject.AddPair(key, TJSONNumber.Create(BcdToDouble(ADataSet.Fields[I].AsBcd)));
2013-10-30 00:48:23 +01:00
end
else
AJSONObject.AddPair(key, TJSONNull.Create);
end;
TFieldType.ftGraphic, TFieldType.ftBlob, TFieldType.ftStream:
begin
if not ADataSet.Fields[I].IsNull then
begin
MS := TMemoryStream.Create;
try
TBlobField(ADataSet.Fields[I]).SaveToStream(MS);
MS.Position := 0;
SS := TStringStream.Create('', TEncoding.ASCII);
try
EncodeStream(MS, SS);
SS.Position := 0;
AJSONObject.AddPair(key, SS.DataString);
finally
SS.Free;
end;
finally
MS.Free;
end;
end
else
AJSONObject.AddPair(key, TJSONNull.Create);
end;
2013-10-30 00:48:23 +01:00
// else
// raise Exception.Create('Cannot find type for field ' + key);
2013-10-30 00:48:23 +01:00
end;
end;
if ADataSetInstanceOwner then
FreeAndNil(ADataSet);
end;
class procedure Mapper.DataSetToObject(ADataSet: TDataSet; AObject: TObject);
var
2013-11-10 01:04:17 +01:00
_type: TRttiType;
_fields: TArray<TRttiProperty>;
_field: TRttiProperty;
_attribute: TCustomAttribute;
_dict: TDictionary<string, string>;
_keys: TDictionary<string, boolean>;
mf: MapperColumnAttribute;
field_name: string;
Value: TValue;
FoundAttribute: boolean;
2013-10-30 00:48:23 +01:00
FoundTransientAttribute: boolean;
begin
_dict := TDictionary<string, string>.Create();
_keys := TDictionary<string, boolean>.Create();
_type := ctx.GetType(AObject.ClassInfo);
_fields := _type.GetProperties;
for _field in _fields do
begin
FoundAttribute := false;
FoundTransientAttribute := false;
for _attribute in _field.GetAttributes do
begin
if _attribute is MapperColumnAttribute then
begin
FoundAttribute := True;
mf := MapperColumnAttribute(_attribute);
_dict.Add(_field.Name, mf.FieldName);
_keys.Add(_field.Name, mf.IsPK);
end
else if _attribute is MapperTransientAttribute then
FoundTransientAttribute := True;
end;
2014-03-24 13:17:30 +01:00
if ((not FoundAttribute) and (not FoundTransientAttribute)) then
2013-10-30 00:48:23 +01:00
begin
_dict.Add(_field.Name, _field.Name);
_keys.Add(_field.Name, false);
end;
end;
for _field in _fields do
begin
if not _dict.TryGetValue(_field.Name, field_name) then
Continue;
case _field.PropertyType.TypeKind of
2014-03-24 13:17:30 +01:00
tkInteger:
2013-10-30 00:48:23 +01:00
Value := ADataSet.FieldByName(field_name).AsInteger;
2014-03-24 13:17:30 +01:00
tkInt64:
Value := ADataSet.FieldByName(field_name).AsLargeInt;
2013-10-30 00:48:23 +01:00
tkFloat:
Value := ADataSet.FieldByName(field_name).AsFloat;
tkString, tkUString, tkWChar, tkLString, tkWString:
Value := ADataSet.FieldByName(field_name).AsString;
end;
_field.SetValue(AObject, Value);
end;
_dict.Free;
_keys.Free;
end;
2014-04-22 00:20:00 +02:00
class function Mapper.ObjectListToJSONArray<T>(AList: TObjectList<T>; AOwnsInstance: boolean;
AForEach: TJSONObjectActionProc): TJSONArray;
2013-10-30 00:48:23 +01:00
var
I: Integer;
JV: TJSONObject;
2013-10-30 00:48:23 +01:00
begin
Result := TJSONArray.Create;
if Assigned(AList) then
for I := 0 to AList.Count - 1 do
begin
JV := ObjectToJSONObject(AList[I]);
if Assigned(AForEach) then
AForEach(JV);
Result.AddElement(JV);
end;
2013-10-30 00:48:23 +01:00
if AOwnsInstance then
AList.Free;
end;
2014-04-22 00:20:00 +02:00
class function Mapper.ObjectListToJSONArrayOfJSONArray<T>(AList: TObjectList<T>): TJSONArray;
2013-10-30 00:48:23 +01:00
var
I: Integer;
begin
Result := TJSONArray.Create;
for I := 0 to AList.Count - 1 do
Result.AddElement(ObjectToJSONArray(AList[I]));
end;
class function Mapper.ObjectListToJSONArrayString<T>(AList: TObjectList<T>; AOwnsInstance: boolean): string;
2014-03-24 13:17:30 +01:00
var
Arr: TJSONArray;
begin
Arr := Mapper.ObjectListToJSONArray<T>(AList, AOwnsInstance);
try
Result := Arr.ToString;
finally
Arr.Free;
end;
end;
2014-04-22 00:20:00 +02:00
class procedure Mapper.ObjectToDataSet(Obj: TObject; Field: TField; var Value: Variant);
2013-10-30 00:48:23 +01:00
begin
Value := GetProperty(Obj, Field.FieldName).AsVariant;
end;
class function Mapper.ObjectToJSONArray(AObject: TObject): TJSONArray;
var
2013-11-10 01:04:17 +01:00
_type: TRttiType;
_fields: TArray<TRttiProperty>;
_field: TRttiProperty;
f: string;
2013-10-30 00:48:23 +01:00
JSONArray: TJSONArray;
2013-11-10 01:04:17 +01:00
o: TObject;
list: IWrappedList;
2014-03-24 13:17:30 +01:00
Arr: TJSONArray;
2013-11-10 01:04:17 +01:00
Obj: TObject;
2013-10-30 00:48:23 +01:00
begin
JSONArray := TJSONArray.Create;
_type := ctx.GetType(AObject.ClassInfo);
_fields := _type.GetProperties;
for _field in _fields do
begin
if HasAttribute<DoNotSerializeAttribute>(_field) then
2013-10-30 00:48:23 +01:00
Continue;
f := GetKeyName(_field, _type);
case _field.PropertyType.TypeKind of
tkEnumeration:
begin
if _field.PropertyType.QualifiedName = 'System.Boolean' then
begin
if _field.GetValue(AObject).AsBoolean then
JSONArray.AddElement(TJSONTrue.Create)
else
JSONArray.AddElement(TJSONFalse.Create)
end;
end;
tkInteger, tkInt64:
2014-04-22 00:20:00 +02:00
JSONArray.AddElement(TJSONNumber.Create(_field.GetValue(AObject).AsInteger));
2013-10-30 00:48:23 +01:00
tkFloat:
begin
if _field.PropertyType.QualifiedName = 'System.TDate' then
2014-04-22 00:20:00 +02:00
JSONArray.AddElement(TJSONString.Create(ISODateToString(_field.GetValue(AObject).AsExtended)))
2013-10-30 00:48:23 +01:00
else if _field.PropertyType.QualifiedName = 'System.TDateTime' then
2014-04-22 00:20:00 +02:00
JSONArray.AddElement(TJSONString.Create(ISODateTimeToString(_field.GetValue(AObject).AsExtended)))
2013-10-30 00:48:23 +01:00
else if _field.PropertyType.QualifiedName = 'System.TTime' then
2014-04-22 00:20:00 +02:00
JSONArray.AddElement(TJSONString.Create(ISOTimeToString(_field.GetValue(AObject).AsExtended)))
2013-10-30 00:48:23 +01:00
else
2014-04-22 00:20:00 +02:00
JSONArray.AddElement(TJSONNumber.Create(_field.GetValue(AObject).AsExtended));
2013-10-30 00:48:23 +01:00
end;
tkString, tkLString, tkWString, tkUString:
2014-04-22 00:20:00 +02:00
JSONArray.AddElement(TJSONString.Create(_field.GetValue(AObject).AsString));
2013-10-30 00:48:23 +01:00
tkClass:
begin
o := _field.GetValue(AObject).AsObject;
if Assigned(o) then
begin
list := nil;
if TDuckTypedList.CanBeWrappedAsList(o) then
list := WrapAsList(o);
if Assigned(list) then
begin
2014-03-24 13:17:30 +01:00
Arr := TJSONArray.Create;
JSONArray.AddElement(Arr);
2013-10-30 00:48:23 +01:00
for Obj in list do
begin
2014-03-24 13:17:30 +01:00
Arr.AddElement(ObjectToJSONObject(Obj));
2013-10-30 00:48:23 +01:00
end;
end
else
begin
2014-04-22 00:20:00 +02:00
JSONArray.AddElement(ObjectToJSONObject(_field.GetValue(AObject).AsObject));
2013-10-30 00:48:23 +01:00
end;
end
else
JSONArray.AddElement(TJSONNull.Create);
end;
end;
end;
Result := JSONArray;
end;
2014-04-22 00:20:00 +02:00
class function Mapper.ObjectToJSONObject(AObject: TObject; AIgnoredProperties: array of string): TJSONObject;
2013-10-30 00:48:23 +01:00
var
2013-11-10 01:04:17 +01:00
_type: TRttiType;
_properties: TArray<TRttiProperty>;
_property: TRttiProperty;
f: string;
JSONObject: TJSONObject;
2014-03-24 13:17:30 +01:00
Arr: TJSONArray;
2013-11-10 01:04:17 +01:00
list: IWrappedList;
Obj, o: TObject;
DoNotSerializeThis: boolean;
I: Integer;
2013-10-30 00:48:23 +01:00
ThereAreIgnoredProperties: boolean;
ts: TTimeStamp;
2014-04-10 13:56:23 +02:00
sr: TStreamReader;
SS: TStringStream;
EncBytes: TBytes;
enc: TEncoding;
2013-10-30 00:48:23 +01:00
begin
ThereAreIgnoredProperties := Length(AIgnoredProperties) > 0;
JSONObject := TJSONObject.Create;
_type := ctx.GetType(AObject.ClassInfo);
_properties := _type.GetProperties;
for _property in _properties do
begin
// f := LowerCase(_property.Name);
f := GetKeyName(_property, _type);
// Delete(f, 1, 1);
if ThereAreIgnoredProperties then
begin
DoNotSerializeThis := false;
for I := low(AIgnoredProperties) to high(AIgnoredProperties) do
if SameText(f, AIgnoredProperties[I]) then
begin
DoNotSerializeThis := True;
Break;
end;
if DoNotSerializeThis then
Continue;
end;
2013-11-11 01:11:09 +01:00
if HasAttribute<DoNotSerializeAttribute>(_property) then
2013-11-11 01:11:09 +01:00
Continue;
2013-10-30 00:48:23 +01:00
case _property.PropertyType.TypeKind of
tkInteger, tkInt64:
2014-04-22 00:20:00 +02:00
JSONObject.AddPair(f, TJSONNumber.Create(_property.GetValue(AObject).AsInteger));
2013-10-30 00:48:23 +01:00
tkFloat:
begin
if _property.PropertyType.QualifiedName = 'System.TDate' then
begin
if _property.GetValue(AObject).AsExtended = 0 then
JSONObject.AddPair(f, TJSONNull.Create)
else
2014-04-22 00:20:00 +02:00
JSONObject.AddPair(f, ISODateToString(_property.GetValue(AObject).AsExtended))
2013-10-30 00:48:23 +01:00
end
else if _property.PropertyType.QualifiedName = 'System.TDateTime' then
begin
if _property.GetValue(AObject).AsExtended = 0 then
JSONObject.AddPair(f, TJSONNull.Create)
else
2014-04-22 00:20:00 +02:00
JSONObject.AddPair(f, ISODateTimeToString(_property.GetValue(AObject).AsExtended))
2013-10-30 00:48:23 +01:00
end
else if _property.PropertyType.QualifiedName = 'System.TTime' then
2014-04-22 00:20:00 +02:00
JSONObject.AddPair(f, ISOTimeToString(_property.GetValue(AObject).AsExtended))
2013-10-30 00:48:23 +01:00
else
2014-04-22 00:20:00 +02:00
JSONObject.AddPair(f, TJSONNumber.Create(_property.GetValue(AObject).AsExtended));
2013-10-30 00:48:23 +01:00
end;
tkString, tkLString, tkWString, tkUString:
JSONObject.AddPair(f, _property.GetValue(AObject).AsString);
tkEnumeration:
begin
if _property.PropertyType.QualifiedName = 'System.Boolean' then
begin
if _property.GetValue(AObject).AsBoolean then
JSONObject.AddPair(f, TJSONTrue.Create)
else
JSONObject.AddPair(f, TJSONFalse.Create);
end
else
begin
2014-04-22 00:20:00 +02:00
JSONObject.AddPair(f, TJSONNumber.Create(_property.GetValue(AObject).AsOrdinal));
2013-10-30 00:48:23 +01:00
end;
end;
tkRecord:
begin
2014-04-22 00:20:00 +02:00
if _property.PropertyType.QualifiedName = 'System.SysUtils.TTimeStamp' then
begin
2014-04-22 00:20:00 +02:00
ts := _property.GetValue(AObject).AsType<System.SysUtils.TTimeStamp>;
JSONObject.AddPair(f, TJSONNumber.Create(TimeStampToMsecs(ts)));
end;
end;
2013-10-30 00:48:23 +01:00
tkClass:
begin
o := _property.GetValue(AObject).AsObject;
if Assigned(o) then
begin
if TDuckTypedList.CanBeWrappedAsList(o) then
begin
list := WrapAsList(o);
if Assigned(list) then
begin
2014-03-24 13:17:30 +01:00
Arr := TJSONArray.Create;
JSONObject.AddPair(f, Arr);
2013-10-30 00:48:23 +01:00
for Obj in list do
begin
2014-04-16 22:52:25 +02:00
if Assigned(Obj) then
// nil element into the list are not serialized
2014-03-24 13:17:30 +01:00
Arr.AddElement(ObjectToJSONObject(Obj));
2013-10-30 00:48:23 +01:00
end;
end
end
2014-04-10 13:56:23 +02:00
else if o is TStream then
begin
if HasAttribute<MapperSerializeAsString>(_property) then
begin
// serialize the stream as a normal string...
TStream(o).Position := 0;
SetLength(EncBytes, Min(TStream(o).Size, 10));
TStream(o).Read(EncBytes, Length(EncBytes));
TStream(o).Position := 0;
TEncoding.GetBufferEncoding(EncBytes, enc);
sr := TStreamReader.Create(TStream(o), enc);
2014-04-10 13:56:23 +02:00
try
JSONObject.AddPair(f, sr.ReadToEnd);
finally
sr.Free;
end;
end
else
begin
// serialize the stream as Base64 encoded string...
TStream(o).Position := 0;
SS := TStringStream.Create;
try
EncodeStream(TStream(o), SS);
JSONObject.AddPair(f, SS.DataString);
finally
SS.Free;
end;
end;
end
2013-10-30 00:48:23 +01:00
else
begin
2014-04-22 00:20:00 +02:00
JSONObject.AddPair(f, ObjectToJSONObject(_property.GetValue(AObject).AsObject));
2013-10-30 00:48:23 +01:00
end;
end
else
JSONObject.AddPair(f, TJSONNull.Create);
end;
end;
end;
Result := JSONObject;
end;
2014-03-24 13:17:30 +01:00
class function Mapper.ObjectToJSONObject(AObject: TObject): TJSONObject;
2013-10-30 00:48:23 +01:00
begin
Result := ObjectToJSONObject(AObject, []);
end;
class function Mapper.ObjectToJSONObjectFields(AObject: TObject; AIgnoredProperties: array of string)
: TJSONObject;
2013-10-30 00:48:23 +01:00
var
2013-11-10 01:04:17 +01:00
_type: TRttiType;
_fields: TArray<TRttiField>;
_field: TRttiField;
f: string;
JSONObject: TJSONObject;
2014-03-24 13:17:30 +01:00
Arr: TJSONArray;
2013-11-10 01:04:17 +01:00
list: IWrappedList;
Obj, o: TObject;
DoNotSerializeThis: boolean;
I: Integer;
2013-10-30 00:48:23 +01:00
ThereAreIgnoredProperties: boolean;
begin
ThereAreIgnoredProperties := Length(AIgnoredProperties) > 0;
JSONObject := TJSONObject.Create;
_type := ctx.GetType(AObject.ClassInfo);
_fields := _type.GetFields;
for _field in _fields do
begin
// f := LowerCase(_field.Name);
f := GetKeyName(_field, _type);
// Delete(f, 1, 1);
if ThereAreIgnoredProperties then
begin
DoNotSerializeThis := false;
for I := low(AIgnoredProperties) to high(AIgnoredProperties) do
if SameText(f, AIgnoredProperties[I]) then
begin
DoNotSerializeThis := True;
Break;
end;
if DoNotSerializeThis then
Continue;
end;
case _field.FieldType.TypeKind of
tkInteger, tkInt64:
2014-04-22 00:20:00 +02:00
JSONObject.AddPair(f, TJSONNumber.Create(_field.GetValue(AObject).AsInteger));
2013-10-30 00:48:23 +01:00
tkFloat:
begin
if _field.FieldType.QualifiedName = 'System.TDate' then
2014-04-22 00:20:00 +02:00
JSONObject.AddPair(f, ISODateToString(_field.GetValue(AObject).AsExtended))
2013-10-30 00:48:23 +01:00
else if _field.FieldType.QualifiedName = 'System.TDateTime' then
2014-04-22 00:20:00 +02:00
JSONObject.AddPair(f, ISODateTimeToString(_field.GetValue(AObject).AsExtended))
2013-10-30 00:48:23 +01:00
else if _field.FieldType.QualifiedName = 'System.TTime' then
2014-04-22 00:20:00 +02:00
JSONObject.AddPair(f, ISOTimeToString(_field.GetValue(AObject).AsExtended))
2013-10-30 00:48:23 +01:00
else
2014-04-22 00:20:00 +02:00
JSONObject.AddPair(f, TJSONNumber.Create(_field.GetValue(AObject).AsExtended));
2013-10-30 00:48:23 +01:00
end;
tkString, tkLString, tkWString, tkUString:
JSONObject.AddPair(f, _field.GetValue(AObject).AsString);
tkEnumeration:
begin
if _field.FieldType.QualifiedName = 'System.Boolean' then
begin
if _field.GetValue(AObject).AsBoolean then
JSONObject.AddPair(f, TJSONTrue.Create)
else
JSONObject.AddPair(f, TJSONFalse.Create);
end
else
begin
2014-04-22 00:20:00 +02:00
JSONObject.AddPair(f, TJSONNumber.Create(_field.GetValue(AObject).AsOrdinal));
2013-10-30 00:48:23 +01:00
end;
end;
tkClass:
begin
o := _field.GetValue(AObject).AsObject;
if Assigned(o) then
begin
list := WrapAsList(o);
if Assigned(list) then
begin
2014-03-24 13:17:30 +01:00
Arr := TJSONArray.Create;
JSONObject.AddPair(f, Arr);
2013-10-30 00:48:23 +01:00
for Obj in list do
begin
2014-03-24 13:17:30 +01:00
Arr.AddElement(ObjectToJSONObject(Obj));
2013-10-30 00:48:23 +01:00
end;
end
else
begin
2014-04-22 00:20:00 +02:00
JSONObject.AddPair(f, ObjectToJSONObject(_field.GetValue(AObject).AsObject));
2013-10-30 00:48:23 +01:00
end;
end
else
JSONObject.AddPair(f, TJSONNull.Create);
end;
end;
end;
Result := JSONObject;
end;
class function Mapper.ObjectToJSONObjectString(AObject: TObject): string;
2014-03-24 13:17:30 +01:00
var
JObj: TJSONObject;
begin
JObj := ObjectToJSONObject(AObject);
try
Result := JObj.ToString;
finally
JObj.Free;
end;
end;
2014-04-22 00:20:00 +02:00
class function Mapper.PropertyExists(JSONObject: TJSONObject; PropertyName: string): boolean;
2014-04-10 13:56:23 +02:00
begin
Result := Assigned(GetPair(JSONObject, PropertyName));
end;
2014-04-22 00:20:00 +02:00
class function Mapper.GetKeyName(const ARttiField: TRttiField; AType: TRttiType): string;
2013-10-30 00:48:23 +01:00
var
attrs: TArray<TCustomAttribute>;
2013-11-10 01:04:17 +01:00
attr: TCustomAttribute;
2013-10-30 00:48:23 +01:00
begin
// JSONSer property attribute handling
attrs := ARttiField.GetAttributes;
for attr in attrs do
begin
if attr is MapperJSONSer then
Exit(MapperJSONSer(attr).Name);
end;
// JSONNaming class attribute handling
attrs := AType.GetAttributes;
for attr in attrs do
begin
if attr is MapperJSONNaming then
begin
case MapperJSONNaming(attr).GetKeyCase of
JSONNameUpperCase:
begin
Exit(UpperCase(ARttiField.Name));
end;
JSONNameLowerCase:
begin
Exit(LowerCase(ARttiField.Name));
end;
end;
end;
end;
// Default
Result := ARttiField.Name;
end;
class function Mapper.GetBooleanDef(JSONObject: TJSONObject; PropertyName: string;
DefaultValue: boolean): boolean;
2013-10-30 00:48:23 +01:00
var
pair: TJSONPair;
begin
pair := GetPair(JSONObject, PropertyName);
if pair = nil then
Exit(DefaultValue);
if pair.JsonValue is TJSONFalse then
Exit(false)
else if pair.JsonValue is TJSONTrue then
Exit(True)
else
2014-04-22 00:20:00 +02:00
raise Exception.CreateFmt('Property %s is not a Boolean Property', [PropertyName]);
2013-10-30 00:48:23 +01:00
end;
2014-04-22 00:20:00 +02:00
class function Mapper.GetInt64Def(JSONObject: TJSONObject; PropertyName: string; DefaultValue: Int64): Int64;
2013-10-30 00:48:23 +01:00
var
pair: TJSONPair;
begin
pair := GetPair(JSONObject, PropertyName);
if pair = nil then
Exit(DefaultValue);
if pair.JsonValue is TJSONNumber then
Exit(TJSONNumber(pair.JsonValue).AsInt64)
else
2014-04-22 00:20:00 +02:00
raise Exception.CreateFmt('Property %s is not a Int64 Property', [PropertyName]);
2013-10-30 00:48:23 +01:00
end;
class function Mapper.GetIntegerDef(JSONObject: TJSONObject; PropertyName: string;
DefaultValue: Integer): Integer;
2013-10-30 00:48:23 +01:00
var
pair: TJSONPair;
begin
pair := GetPair(JSONObject, PropertyName);
if pair = nil then
Exit(DefaultValue);
if pair.JsonValue is TJSONNumber then
Exit(TJSONNumber(pair.JsonValue).AsInt)
else
2014-04-22 00:20:00 +02:00
raise Exception.CreateFmt('Property %s is not an Integer Property', [PropertyName]);
2013-10-30 00:48:23 +01:00
end;
2014-04-22 00:20:00 +02:00
class function Mapper.GetJSONArray(JSONObject: TJSONObject; PropertyName: string): TJSONArray;
2013-10-30 00:48:23 +01:00
var
pair: TJSONPair;
begin
pair := GetPair(JSONObject, PropertyName);
if pair = nil then
Exit(nil);
if pair.JsonValue is TJSONArray then
Exit(TJSONArray(pair.JsonValue))
else
raise Exception.Create('Property is not a JSONArray');
end;
2014-04-22 00:20:00 +02:00
class function Mapper.GetJSONObj(JSONObject: TJSONObject; PropertyName: string): TJSONObject;
2013-10-30 00:48:23 +01:00
var
pair: TJSONPair;
begin
pair := GetPair(JSONObject, PropertyName);
if pair = nil then
Exit(nil);
if pair.JsonValue is TJSONObject then
Exit(TJSONObject(pair.JsonValue))
else
raise Exception.Create('Property is not a JSONObject');
end;
2014-04-22 00:20:00 +02:00
class function Mapper.GetKeyName(const ARttiProp: TRttiProperty; AType: TRttiType): string;
2013-10-30 00:48:23 +01:00
var
attrs: TArray<TCustomAttribute>;
2013-11-10 01:04:17 +01:00
attr: TCustomAttribute;
2013-10-30 00:48:23 +01:00
begin
// JSONSer property attribute handling
attrs := ARttiProp.GetAttributes;
for attr in attrs do
begin
if attr is MapperJSONSer then
Exit(MapperJSONSer(attr).Name);
end;
// JSONNaming class attribute handling
attrs := AType.GetAttributes;
for attr in attrs do
begin
if attr is MapperJSONNaming then
begin
case MapperJSONNaming(attr).GetKeyCase of
JSONNameUpperCase:
begin
Exit(UpperCase(ARttiProp.Name));
end;
JSONNameLowerCase:
begin
Exit(LowerCase(ARttiProp.Name));
end;
end;
end;
end;
// Default
Result := ARttiProp.Name;
end;
class function Mapper.GetNumberDef(JSONObject: TJSONObject; PropertyName: string; DefaultValue: Extended)
: Extended;
2013-10-30 00:48:23 +01:00
var
pair: TJSONPair;
begin
pair := GetPair(JSONObject, PropertyName);
if pair = nil then
Exit(DefaultValue);
if pair.JsonValue is TJSONNumber then
Exit(TJSONNumber(pair.JsonValue).AsDouble)
else
raise Exception.Create('Property is not a Number Property');
end;
2014-04-22 00:20:00 +02:00
class function Mapper.GetPair(JSONObject: TJSONObject; PropertyName: string): TJSONPair;
2013-10-30 00:48:23 +01:00
var
pair: TJSONPair;
begin
if not Assigned(JSONObject) then
raise Exception.Create('JSONObject is nil');
pair := JSONObject.Get(PropertyName);
Result := pair;
end;
2014-04-22 00:20:00 +02:00
class function Mapper.GetProperty(Obj: TObject; const PropertyName: string): TValue;
2013-10-30 00:48:23 +01:00
var
2013-11-10 01:04:17 +01:00
Prop: TRttiProperty;
2013-10-30 00:48:23 +01:00
ARTTIType: TRttiType;
begin
ARTTIType := ctx.GetType(Obj.ClassType);
if not Assigned(ARTTIType) then
2014-04-22 00:20:00 +02:00
raise Exception.CreateFmt('Cannot get RTTI for type [%s]', [ARTTIType.ToString]);
2013-10-30 00:48:23 +01:00
Prop := ARTTIType.GetProperty(PropertyName);
if not Assigned(Prop) then
2014-04-22 00:20:00 +02:00
raise Exception.CreateFmt('Cannot get RTTI for property [%s.%s]', [ARTTIType.ToString, PropertyName]);
2013-10-30 00:48:23 +01:00
if Prop.IsReadable then
Result := Prop.GetValue(Obj)
else
2014-04-22 00:20:00 +02:00
raise Exception.CreateFmt('Property is not readable [%s.%s]', [ARTTIType.ToString, PropertyName]);
2013-10-30 00:48:23 +01:00
end;
2014-04-22 00:20:00 +02:00
class function Mapper.GetStringDef(JSONObject: TJSONObject; PropertyName, DefaultValue: string): string;
2013-10-30 00:48:23 +01:00
var
pair: TJSONPair;
begin
pair := GetPair(JSONObject, PropertyName);
if pair = nil then
Exit(DefaultValue);
if pair.JsonValue is TJSONString then
Exit(TJSONString(pair.JsonValue).Value)
else
raise Exception.Create('Property is not a String Property');
end;
class function Mapper.HasAttribute<T>(ARTTIMember: TRttiNamedObject; out AAttribute: T): boolean;
2013-10-30 00:48:23 +01:00
var
attrs: TArray<TCustomAttribute>;
2013-11-10 01:04:17 +01:00
attr: TCustomAttribute;
2013-10-30 00:48:23 +01:00
begin
AAttribute := nil;
Result := false;
attrs := ARTTIMember.GetAttributes;
for attr in attrs do
if attr is T then
begin
AAttribute := T(attr);
Exit(True);
end;
end;
class function Mapper.HasAttribute<T>(ARTTIMember: TRttiNamedObject): boolean;
2013-10-30 00:48:23 +01:00
var
attrs: TArray<TCustomAttribute>;
2013-11-10 01:04:17 +01:00
attr: TCustomAttribute;
2013-10-30 00:48:23 +01:00
begin
Result := false;
attrs := ARTTIMember.GetAttributes;
for attr in attrs do
if attr is T then
Exit(True);
end;
class procedure Mapper.JSONArrayToDataSet(AJSONArray: TJSONArray; ADataSet: TDataSet;
AJSONArrayInstanceOwner: boolean);
2014-03-24 13:17:30 +01:00
begin
JSONArrayToDataSet(AJSONArray, ADataSet, TArray<string>.Create(), AJSONArrayInstanceOwner);
2014-03-24 13:17:30 +01:00
end;
class procedure Mapper.JSONArrayToDataSet(AJSONArray: TJSONArray; ADataSet: TDataSet;
AIgnoredFields: TArray<string>;
2014-03-24 13:17:30 +01:00
AJSONArrayInstanceOwner: boolean);
var
I: Integer;
begin
for I := 0 to AJSONArray.Size - 1 do
begin
2014-03-24 13:17:30 +01:00
ADataSet.Append;
2014-04-22 00:20:00 +02:00
Mapper.JSONObjectToDataSet(AJSONArray.Get(I) as TJSONObject, ADataSet, AIgnoredFields, false);
ADataSet.Post;
end;
if AJSONArrayInstanceOwner then
AJSONArray.Free;
end;
2014-04-22 00:20:00 +02:00
class procedure Mapper.JSONArrayToObjectList<T>(AList: TObjectList<T>; AJSONArray: TJSONArray;
AInstanceOwner, AOwnsChildObjects: boolean);
2013-10-30 00:48:23 +01:00
var
I: Integer;
begin
if Assigned(AJSONArray) then
begin
for I := 0 to AJSONArray.Size - 1 do
AList.Add(Mapper.JSONObjectToObject<T>(AJSONArray.Get(I) as TJSONObject));
2013-10-30 00:48:23 +01:00
if AInstanceOwner then
AJSONArray.Free;
end;
end;
2014-04-22 00:20:00 +02:00
class function Mapper.JSONArrayToObjectList<T>(AJSONArray: TJSONArray; AInstanceOwner: boolean;
AOwnsChildObjects: boolean): TObjectList<T>;
begin
Result := TObjectList<T>.Create(AOwnsChildObjects);
2014-04-22 00:20:00 +02:00
JSONArrayToObjectList<T>(Result, AJSONArray, AInstanceOwner, AOwnsChildObjects);
end;
class procedure Mapper.InternalJSONObjectToObject(ctx: TRTTIContext; AJSONObject: TJSONObject;
AObject: TObject);
2013-10-30 00:48:23 +01:00
var
2013-11-10 01:04:17 +01:00
_type: TRttiType;
2013-10-30 00:48:23 +01:00
_fields: TArray<TRttiProperty>;
2013-11-10 01:04:17 +01:00
_field: TRttiProperty;
f: string;
jvalue: TJSONValue;
v: TValue;
o: TObject;
list: IWrappedList;
I: Integer;
cref: TClass;
attr: MapperItemsClassType;
2014-03-24 13:17:30 +01:00
Arr: TJSONArray;
n: TJSONNumber;
SerStreamASString: string;
EncBytes: TBytes;
enc: TEncoding;
sw: TStreamWriter;
2013-10-30 00:48:23 +01:00
begin
2014-03-07 23:16:33 +01:00
jvalue := nil;
2013-10-30 00:48:23 +01:00
_type := ctx.GetType(AObject.ClassInfo);
_fields := _type.GetProperties;
for _field in _fields do
begin
2014-04-22 00:20:00 +02:00
if ((not _field.IsWritable) and (_field.PropertyType.TypeKind <> tkClass)) or
(HasAttribute<MapperTransientAttribute>(_field)) then
2013-10-30 00:48:23 +01:00
Continue;
f := GetKeyName(_field, _type);
if Assigned(AJSONObject.Get(f)) then
jvalue := AJSONObject.Get(f).JsonValue
else
Continue;
case _field.PropertyType.TypeKind of
tkEnumeration:
begin
if _field.PropertyType.QualifiedName = 'System.Boolean' then
begin
if jvalue is TJSONTrue then
_field.SetValue(TObject(AObject), True)
else if jvalue is TJSONFalse then
_field.SetValue(TObject(AObject), false)
else
2014-04-22 00:20:00 +02:00
raise Exception.Create('Invalid value for property ' + _field.Name);
2013-10-30 00:48:23 +01:00
end
else // it is an enumerated value but it's not a boolean.
begin
2014-04-22 00:20:00 +02:00
TValue.Make((jvalue as TJSONNumber).AsInt, _field.PropertyType.Handle, v);
2013-10-30 00:48:23 +01:00
_field.SetValue(TObject(AObject), v);
end;
end;
tkInteger, tkInt64:
_field.SetValue(TObject(AObject), StrToIntDef(jvalue.Value, 0));
tkFloat:
begin
if _field.PropertyType.QualifiedName = 'System.TDate' then
begin
if jvalue is TJSONNull then
_field.SetValue(TObject(AObject), 0)
else
2014-04-22 00:20:00 +02:00
_field.SetValue(TObject(AObject), ISOStrToDateTime(jvalue.Value + ' 00:00:00'))
2013-10-30 00:48:23 +01:00
end
else if _field.PropertyType.QualifiedName = 'System.TDateTime' then
begin
if jvalue is TJSONNull then
_field.SetValue(TObject(AObject), 0)
else
_field.SetValue(TObject(AObject), ISOStrToDateTime(jvalue.Value))
end
else if _field.PropertyType.QualifiedName = 'System.TTime' then
_field.SetValue(TObject(AObject), ISOStrToTime(jvalue.Value))
2014-06-27 15:30:39 +02:00
else if _field.PropertyType.QualifiedName = 'System.Currency' then
_field.SetValue(TObject(AObject), (jvalue as TJSONNumber).AsDouble)
2013-10-30 00:48:23 +01:00
else
_field.SetValue(TObject(AObject), (jvalue as TJSONNumber).AsDouble)
end;
tkString, tkLString, tkWString, tkUString:
begin
_field.SetValue(TObject(AObject), jvalue.Value);
end;
tkRecord:
begin
2014-04-22 00:20:00 +02:00
if _field.PropertyType.QualifiedName = 'System.SysUtils.TTimeStamp' then
begin
n := jvalue as TJSONNumber;
2014-04-22 00:20:00 +02:00
_field.SetValue(TObject(AObject), TValue.From<TTimeStamp>(MSecsToTimeStamp(n.AsInt64)));
end;
end;
2013-10-30 00:48:23 +01:00
tkClass: // try to restore child properties... but only if the collection is not nil!!!
begin
o := _field.GetValue(TObject(AObject)).AsObject;
if Assigned(o) then
begin
if o is TStream then
begin
if jvalue is TJSONString then
begin
SerStreamASString := TJSONString(jvalue).Value;
end
else
raise Exception.Create('Expected JSONString in ' + AJSONObject.Get(f).JsonString.Value);
if HasAttribute<MapperSerializeAsString>(_field) then
begin
// serialize the stream as a normal string...
TStream(o).Position := 0;
sw := TStreamWriter.Create(TStream(o));
try
sw.Write(SerStreamASString);
finally
sw.Free;
end;
end
else
begin
// deserialize the stream as Base64 encoded string...
TStream(o).Position := 0;
sw := TStreamWriter.Create(TStream(o));
try
sw.Write(DecodeString(SerStreamASString));
finally
sw.Free;
end;
end;
end
else
if TDuckTypedList.CanBeWrappedAsList(o) then
2013-10-30 00:48:23 +01:00
begin // restore collection
if jvalue is TJSONArray then
begin
2014-03-24 13:17:30 +01:00
Arr := TJSONArray(jvalue);
// look for the MapperItemsClassType on the property itself or on the property type
if Mapper.HasAttribute<MapperItemsClassType>(_field, attr) or
Mapper.HasAttribute<MapperItemsClassType>(_field.PropertyType, attr) then
2013-10-30 00:48:23 +01:00
begin
cref := attr.Value;
list := WrapAsList(o);
2014-03-24 13:17:30 +01:00
for I := 0 to Arr.Size - 1 do
2013-10-30 00:48:23 +01:00
begin
2014-04-22 00:20:00 +02:00
list.Add(Mapper.JSONObjectToObject(cref, Arr.Get(I) as TJSONObject));
2013-10-30 00:48:23 +01:00
end;
end;
end
else
raise Exception.Create('Cannot restore ' + f +
' because the related json property is not an array');
2013-10-30 00:48:23 +01:00
end
else // try to deserialize into the property... but the json MUST be an object
begin
if jvalue is TJSONObject then
begin
InternalJSONObjectToObject(ctx, TJSONObject(jvalue), o);
end
else
raise Exception.Create('Cannot deserialize property ' + _field.Name);
end;
2013-10-30 00:48:23 +01:00
end;
end;
end;
end;
end;
2014-04-22 00:20:00 +02:00
class function Mapper.JSONObjectToObject(Clazz: TClass; AJSONObject: TJSONObject): TObject;
2013-10-30 00:48:23 +01:00
var
AObject: TObject;
begin
AObject := TRTTIUtils.CreateObject(Clazz.QualifiedClassName);
try
InternalJSONObjectToObject(ctx, AJSONObject, AObject);
Result := AObject;
except
AObject.Free;
Result := nil;
end;
end;
2014-04-22 00:20:00 +02:00
class procedure Mapper.JSONObjectToDataSet(AJSONObject: TJSONObject; ADataSet: TDataSet;
AJSONObjectInstanceOwner: boolean);
2014-03-24 13:17:30 +01:00
begin
JSONObjectToDataSet(AJSONObject, ADataSet, TArray<string>.Create(), AJSONObjectInstanceOwner);
2014-03-24 13:17:30 +01:00
end;
class function Mapper.JSONObjectStringToObject<T>(const AJSONObjectString: string): T;
2014-04-10 13:56:23 +02:00
var
JObj: TJSONObject;
begin
JObj := TJSONObject.ParseJSONValue(AJSONObjectString) as TJSONObject;
try
Result := JSONObjectToObject<T>(JObj);
finally
JObj.Free;
end;
end;
class procedure Mapper.JSONObjectToDataSet(AJSONObject: TJSONObject; ADataSet: TDataSet;
AIgnoredFields: TArray<string>;
2014-03-24 13:17:30 +01:00
AJSONObjectInstanceOwner: boolean);
var
I: Integer;
key: string;
v: TJSONValue;
jp: TJSONPair;
fs: TFormatSettings;
MS: TMemoryStream;
SS: TStringStream;
begin
for I := 0 to ADataSet.FieldCount - 1 do
begin
2014-03-24 13:17:30 +01:00
if ContainsFieldName(ADataSet.Fields[I].FieldName, AIgnoredFields) then
Continue;
key := LowerCase(ADataSet.Fields[I].FieldName);
v := nil;
jp := AJSONObject.Get(key);
if Assigned(jp) then
if not(jp.JsonValue is TJSONNull) then
v := AJSONObject.Get(key).JsonValue;
if not Assigned(v) then
begin
ADataSet.Fields[I].Clear;
Continue;
end;
case ADataSet.Fields[I].DataType of
2014-04-22 00:20:00 +02:00
TFieldType.ftInteger, TFieldType.ftAutoInc, TFieldType.ftSmallint, TFieldType.ftShortint:
begin
ADataSet.Fields[I].AsInteger := (v as TJSONNumber).AsInt;
end;
TFieldType.ftLargeint:
begin
ADataSet.Fields[I].AsLargeInt := (v as TJSONNumber).AsInt64;
end;
TFieldType.ftSingle, TFieldType.ftFloat:
begin
ADataSet.Fields[I].AsFloat := (v as TJSONNumber).AsDouble;
end;
ftString, ftWideString, ftMemo:
begin
ADataSet.Fields[I].AsString := (v as TJSONString).Value;
end;
TFieldType.ftDate:
begin
2014-04-22 00:20:00 +02:00
ADataSet.Fields[I].AsDateTime := ISOStrToDate((v as TJSONString).Value);
end;
TFieldType.ftDateTime:
begin
2014-04-22 00:20:00 +02:00
ADataSet.Fields[I].AsDateTime := ISOStrToDateTime((v as TJSONString).Value);
end;
TFieldType.ftTimeStamp:
begin
2014-04-22 00:20:00 +02:00
ADataSet.Fields[I].AsSQLTimeStamp := StrToSQLTimeStamp((v as TJSONString).Value);
end;
TFieldType.ftCurrency:
begin
fs.DecimalSeparator := '.';
2014-04-22 00:20:00 +02:00
ADataSet.Fields[I].AsCurrency := StrToCurr((v as TJSONString).Value, fs);
end;
TFieldType.ftFMTBcd:
begin
ADataSet.Fields[I].AsBcd := DoubleToBcd((v as TJSONNumber).AsDouble);
end;
TFieldType.ftGraphic, TFieldType.ftBlob, TFieldType.ftStream:
begin
MS := TMemoryStream.Create;
try
2014-04-22 00:20:00 +02:00
SS := TStringStream.Create((v as TJSONString).Value, TEncoding.ASCII);
try
DecodeStream(SS, MS);
MS.Position := 0;
TBlobField(ADataSet.Fields[I]).LoadFromStream(MS);
finally
SS.Free;
end;
finally
MS.Free;
end;
end;
// else
// raise Exception.Create('Cannot find type for field ' + key);
end;
end;
if AJSONObjectInstanceOwner then
FreeAndNil(AJSONObject);
end;
2014-04-22 00:20:00 +02:00
class function Mapper.JSONObjectToObject(ClazzName: string; AJSONObject: TJSONObject): TObject;
2013-10-30 00:48:23 +01:00
var
2013-11-10 01:04:17 +01:00
AObject: TObject;
2013-10-30 00:48:23 +01:00
_rttiType: TRttiType;
begin
_rttiType := Mapper.ctx.FindType(ClazzName);
if Assigned(_rttiType) then
begin
AObject := TRTTIUtils.CreateObject(_rttiType);
try
InternalJSONObjectToObject(ctx, AJSONObject, AObject);
Result := AObject;
except
AObject.Free;
Result := nil;
end;
end
else
raise Exception.CreateFmt('Class not found [%s]', [ClazzName]);
end;
2014-04-10 13:56:23 +02:00
class function Mapper.JSONObjectToObject<T>(AJSONObject: TJSONObject): T;
2013-10-30 00:48:23 +01:00
begin
2013-11-18 00:44:40 +01:00
if not Assigned(AJSONObject) then
raise Exception.Create('JSONObject not assigned');
2013-10-30 00:48:23 +01:00
Result := Mapper.JSONObjectToObject(T.QualifiedClassName, AJSONObject) as T;
// Result := JSONObjectToObject(TObject.ClassInfo, AJSONObject);
end;
2014-04-16 22:52:25 +02:00
class function Mapper.JSONObjectToObjectFields<T>(AJSONObject: TJSONObject): T;
2013-10-30 00:48:23 +01:00
var
2013-11-10 01:04:17 +01:00
_type: TRttiType;
2013-10-30 00:48:23 +01:00
_fields: TArray<TRttiField>;
2013-11-10 01:04:17 +01:00
_field: TRttiField;
f: string;
2013-10-30 00:48:23 +01:00
AObject: T;
2013-11-10 01:04:17 +01:00
jvalue: TJSONValue;
2013-10-30 00:48:23 +01:00
begin
AObject := T.Create;
try
_type := ctx.GetType(AObject.ClassInfo);
_fields := _type.GetFields;
for _field in _fields do
begin
f := LowerCase(_field.Name);
Delete(f, 1, 1);
if Assigned(AJSONObject.Get(f)) then
jvalue := AJSONObject.Get(f).JsonValue
else
Continue;
case _field.FieldType.TypeKind of
tkInteger, tkInt64:
_field.SetValue(TObject(AObject), StrToIntDef(jvalue.Value, 0));
tkFloat:
begin
if _field.FieldType.QualifiedName = 'System.TDate' then
_field.SetValue(TObject(AObject), StrToDate(jvalue.Value))
else if _field.FieldType.QualifiedName = 'System.TDateTime' then
_field.SetValue(TObject(AObject), StrToDateTime(jvalue.Value))
else
2014-04-22 00:20:00 +02:00
_field.SetValue(TObject(AObject), (jvalue as TJSONNumber).AsDouble)
2013-10-30 00:48:23 +01:00
end;
tkString, tkLString, tkWString, tkUString:
begin
_field.SetValue(TObject(AObject), jvalue.Value);
end;
end;
end;
Result := AObject;
except
AObject.Free;
AObject := nil;
Result := nil;
end;
end;
2014-04-22 00:20:00 +02:00
class procedure Mapper.DataSetToObjectList<T>(ADataSet: TDataSet; AObjectList: TObjectList<T>;
ACloseDataSetAfterScroll: boolean);
2013-10-30 00:48:23 +01:00
var
2013-11-10 01:04:17 +01:00
Obj: T;
2013-10-30 00:48:23 +01:00
SavedPosition: TArray<Byte>;
begin
ADataSet.DisableControls;
try
SavedPosition := ADataSet.Bookmark;
while not ADataSet.Eof do
begin
Obj := T.Create;
DataSetToObject(ADataSet, Obj);
AObjectList.Add(Obj);
ADataSet.Next;
end;
if ADataSet.BookmarkValid(SavedPosition) then
ADataSet.Bookmark := SavedPosition;
finally
ADataSet.EnableControls;
end;
if ACloseDataSetAfterScroll then
ADataSet.Close;
end;
2013-11-10 01:04:17 +01:00
//
2013-11-11 01:11:09 +01:00
// class procedure Mapper.DataSetToXML(ADataSet: TDataSet;
// XMLDocument: String; ADataSetInstanceOwner: boolean);
// var
// Xml: IXMLDocument;
// Row: IXMLNode;
// begin
// DefaultDOMVendor := 'ADOM XML v4';
// Xml := NewXMLDocument();
// while not ADataSet.Eof do
// begin
// Row := Xml.CreateNode('row');
// // Row := Xml.DocumentElement.AddChild('row');
// // DataSetRowToXML(ADataSet, Row, false);
// Xml.ChildNodes.Add(Row);
// break;
// ADataSet.Next;
// end;
// if ADataSetInstanceOwner then
// FreeAndNil(ADataSet);
// Xml.SaveToXML(XMLDocument);
// end;
//
// class procedure Mapper.DataSetRowToXML(ADataSet: TDataSet;
// Row: IXMLNode; ADataSetInstanceOwner: boolean);
// var
// I: Integer;
// key: string;
// dt: TDateTime;
// tt: TTime;
// Time: TTimeStamp;
// ts: TSQLTimeStamp;
// begin
// for I := 0 to ADataSet.FieldCount - 1 do
// begin
// key := LowerCase(ADataSet.Fields[I].FieldName);
// case ADataSet.Fields[I].DataType of
// TFieldType.ftInteger, TFieldType.ftSmallint, TFieldType.ftShortint:
// Row.Attributes[key] := ADataSet.Fields[I].AsInteger;
// // AJSONObject.AddPair(key, TJSONNumber.Create(ADataSet.Fields[I].AsInteger));
// TFieldType.ftLargeint:
// begin
// Row.Attributes[key] := ADataSet.Fields[I].AsLargeInt;
// end;
// TFieldType.ftSingle, TFieldType.ftFloat:
// Row.Attributes[key] := ADataSet.Fields[I].AsFloat;
// ftString, ftWideString, ftMemo:
// Row.Attributes[key] := ADataSet.Fields[I].AsWideString;
// TFieldType.ftDate:
// begin
// if not ADataSet.Fields[I].IsNull then
// begin
// Row.Attributes[key] := ISODateToString(ADataSet.Fields[I].AsDateTime);
// end
// end;
// TFieldType.ftDateTime:
// begin
// if not ADataSet.Fields[I].IsNull then
// begin
// Row.Attributes[key] := ISODateTimeToString(ADataSet.Fields[I].AsDateTime);
// end
// end;
// TFieldType.ftTimeStamp:
// begin
// if not ADataSet.Fields[I].IsNull then
// begin
// ts := ADataSet.Fields[I].AsSQLTimeStamp;
// Row.Attributes[key] := SQLTimeStampToStr('hh:nn:ss', ts);
// end
// end;
// TFieldType.ftCurrency:
// begin
// if not ADataSet.Fields[I].IsNull then
// begin
// Row.Attributes[key] := FormatCurr('0.00##', ADataSet.Fields[I].AsCurrency);
// end
// end;
// TFieldType.ftFMTBcd:
// begin
// if not ADataSet.Fields[I].IsNull then
// begin
// Row.Attributes[key] := BcdToDouble(ADataSet.Fields[I].AsBcd);
// end
// end
// else
// raise Exception.Create('Cannot find type for field ' + key);
// end;
// end;
// if ADataSetInstanceOwner then
// FreeAndNil(ADataSet);
// end;
2013-11-10 01:04:17 +01:00
2014-04-22 00:20:00 +02:00
{$IF Defined(VER260) or Defined(VER270)}
2014-02-24 10:20:34 +01:00
2014-04-22 00:20:00 +02:00
class procedure Mapper.ObjectToFDParameters(AFDParams: TFDParams; AObject: TObject; AParamPrefix: string);
2014-02-24 10:20:34 +01:00
var
I: Integer;
pname: string;
_rttiType: TRttiType;
obj_fields: TArray<TRttiProperty>;
obj_field: TRttiProperty;
obj_field_attr: MapperColumnAttribute;
Map: TObjectDictionary<string, TRttiProperty>;
f: TRttiProperty;
fv: TValue;
2014-03-24 13:17:30 +01:00
PrefixLength: Integer;
2014-05-23 00:45:56 +02:00
function KindToFieldType(AKind: TTypeKind; AProp: TRttiProperty): TFieldType;
begin
case AKind of
tkInteger:
Result := ftInteger;
tkFloat:
2014-05-23 00:45:56 +02:00
begin // daniele teti 2014-05-23
if AProp.PropertyType.QualifiedName = 'System.TDate' then
Result := ftDate
else if AProp.PropertyType.QualifiedName = 'System.TDateTime' then
Result := ftDateTime
else if AProp.PropertyType.QualifiedName = 'System.TTime' then
Result := ftTime
else
Result := ftFloat;
end;
tkChar,
tkWChar,
tkString,
tkUString,
tkLString,
tkWString:
Result := ftWideString;
tkVariant:
Result := ftVariant;
tkArray:
Result := ftArray;
tkInterface:
Result := ftInterface;
tkInt64:
Result := ftLongWord;
else
Result := ftUnknown;
end;
end;
2014-02-24 10:20:34 +01:00
begin
2014-03-24 13:17:30 +01:00
PrefixLength := Length(AParamPrefix);
2014-02-24 10:20:34 +01:00
Map := TObjectDictionary<string, TRttiProperty>.Create;
try
if Assigned(AObject) then
begin
_rttiType := ctx.GetType(AObject.ClassType);
obj_fields := _rttiType.GetProperties;
for obj_field in obj_fields do
begin
if HasAttribute<MapperColumnAttribute>(obj_field, obj_field_attr) then
begin
2014-04-22 00:20:00 +02:00
Map.Add(MapperColumnAttribute(obj_field_attr).FieldName.ToLower, obj_field);
2014-02-24 10:20:34 +01:00
end
else
begin
Map.Add(obj_field.Name.ToLower, obj_field);
2014-02-24 10:20:34 +01:00
end
end;
end;
for I := 0 to AFDParams.Count - 1 do
begin
pname := AFDParams[I].Name.ToLower;
if pname.StartsWith(AParamPrefix, True) then
2014-03-24 13:17:30 +01:00
Delete(pname, 1, PrefixLength);
2014-02-24 10:20:34 +01:00
if Map.TryGetValue(pname, f) then
begin
fv := f.GetValue(AObject);
2014-05-23 00:45:56 +02:00
AFDParams[I].DataType := KindToFieldType(fv.Kind, f); // DmitryG - 2014-03-28
2014-02-24 10:20:34 +01:00
AFDParams[I].Value := fv.AsVariant;
end
else
begin
AFDParams[I].Clear;
end;
end;
finally
Map.Free;
end
end;
class function Mapper.InternalExecuteFDQuery(AQuery: TFDQuery; AObject: TObject;
WithResult: boolean): Int64;
2014-02-24 10:20:34 +01:00
begin
ObjectToFDParameters(AQuery.Params, AObject);
Result := 0;
if WithResult then
AQuery.Open
else
begin
AQuery.ExecSQL;
Result := AQuery.RowsAffected;
end;
end;
2014-04-22 00:20:00 +02:00
class function Mapper.ExecuteFDQueryNoResult(AQuery: TFDQuery; AObject: TObject): Int64;
2014-02-24 10:20:34 +01:00
begin
Result := InternalExecuteFDQuery(AQuery, AObject, false);
end;
class procedure Mapper.ExecuteFDQuery(AQuery: TFDQuery; AObject: TObject);
begin
InternalExecuteFDQuery(AQuery, AObject, True);
end;
2014-04-22 00:20:00 +02:00
{$ENDIF}
{$IF not Defined(VER270)}
2014-02-24 10:20:34 +01:00
2014-04-22 00:20:00 +02:00
class function Mapper.ExecuteSQLQueryNoResult(AQuery: TSQLQuery; AObject: TObject): Int64;
2013-10-30 00:48:23 +01:00
begin
Result := InternalExecuteSQLQuery(AQuery, AObject, false);
end;
2014-04-16 22:52:25 +02:00
class procedure Mapper.ExecuteSQLQuery(AQuery: TSQLQuery; AObject: TObject);
2013-10-30 00:48:23 +01:00
begin
InternalExecuteSQLQuery(AQuery, AObject, True);
end;
2014-04-22 00:20:00 +02:00
class function Mapper.ExecuteSQLQueryAsObjectList<T>(AQuery: TSQLQuery; AObject: TObject): TObjectList<T>;
2013-10-30 00:48:23 +01:00
begin
ExecuteSQLQuery(AQuery, AObject);
Result := TObjectList<T>.Create(True);
DataSetToObjectList<T>(AQuery, Result);
end;
2014-04-16 22:52:25 +02:00
{$IFEND}
2013-10-30 00:48:23 +01:00
{ MappedField }
constructor MapperColumnAttribute.Create(AFieldName: string; AIsPK: boolean);
begin
inherited Create;
FFieldName := AFieldName;
FIsPK := AIsPK;
end;
procedure MapperColumnAttribute.SetFieldName(const Value: string);
begin
FFieldName := Value;
end;
procedure MapperColumnAttribute.SetIsPK(const Value: boolean);
begin
FIsPK := Value;
end;
{ GridColumnProps }
2014-04-22 00:20:00 +02:00
constructor GridColumnProps.Create(ACaption: string; AAlign: TGridColumnAlign; AWidth: Integer);
2013-10-30 00:48:23 +01:00
begin
inherited Create;
FCaption := ACaption;
FAlign := AAlign;
2013-11-10 01:04:17 +01:00
{$IF CompilerVersion >= 23.0}
2013-10-30 00:48:23 +01:00
FWidth := System.Math.Max(AWidth, 50);
2013-11-10 01:04:17 +01:00
{$ELSE}
2013-10-30 00:48:23 +01:00
FWidth := Math.Max(AWidth, 50);
2013-11-10 01:04:17 +01:00
{$IFEND}
2013-10-30 00:48:23 +01:00
end;
function GridColumnProps.GetAlignAsString: string;
begin
case FAlign of
caLeft:
Result := 'left';
caCenter:
Result := 'center';
caRight:
Result := 'right';
end;
end;
{ JSONSer }
constructor MapperJSONSer.Create(AName: string);
begin
inherited Create;
FName := AName;
end;
function MapperJSONSer.GetName: string;
begin
Result := FName;
end;
{ JSONNaming }
constructor MapperJSONNaming.Create(JSONKeyCase: TJSONNameCase);
begin
inherited Create;
FJSONKeyCase := JSONKeyCase;
end;
function MapperJSONNaming.GetKeyCase: TJSONNameCase;
begin
Result := FJSONKeyCase;
end;
{ StringValueAttribute }
constructor StringValueAttribute.Create(Value: string);
begin
inherited Create;
FValue := Value;
end;
procedure StringValueAttribute.SetValue(const Value: string);
begin
FValue := Value;
end;
{ ItemsClassType }
constructor MapperItemsClassType.Create(Value: TClass);
begin
inherited Create;
FValue := Value;
end;
procedure MapperItemsClassType.SetValue(const Value: TClass);
begin
FValue := Value;
end;
{ TDataSetHelper }
function TDataSetHelper.AsJSONArray: TJSONArray;
var
JArr: TJSONArray;
begin
JArr := TJSONArray.Create;
try
if not Eof then
Mapper.DataSetToJSONArray(Self, JArr, false);
Result := JArr;
except
FreeAndNil(JArr);
raise;
end;
end;
function TDataSetHelper.AsJSONArrayString: string;
2014-03-24 13:17:30 +01:00
var
Arr: TJSONArray;
begin
Arr := AsJSONArray;
try
Result := Arr.ToString;
finally
Arr.Free;
end;
end;
2014-03-13 01:01:27 +01:00
function TDataSetHelper.AsJSONObject(AReturnNilIfEOF: boolean): TJSONObject;
var
JObj: TJSONObject;
begin
JObj := TJSONObject.Create;
try
Mapper.DataSetToJSONObject(Self, JObj, false);
2014-03-13 01:01:27 +01:00
if AReturnNilIfEOF and (JObj.Size = 0) then
FreeAndNil(JObj);
Result := JObj;
except
FreeAndNil(JObj);
raise;
end;
end;
function TDataSetHelper.AsJSONObjectString(AReturnEmptyStringIfEOF: boolean): string;
2014-03-24 13:17:30 +01:00
var
JObj: TJSONObject;
begin
JObj := AsJSONObject(True);
if not Assigned(JObj) then
begin
if AReturnEmptyStringIfEOF then
Result := ''
else
Result := '{}';
end
else
try
Result := JObj.ToString;
finally
JObj.Free;
end;
end;
function TDataSetHelper.AsObject<T>(CloseAfterScroll: boolean): T;
var
Obj: T;
begin
2014-03-24 13:17:30 +01:00
if not Self.Eof then
begin
Obj := T.Create;
try
Mapper.DataSetToObject(Self, Obj);
Result := Obj;
except
FreeAndNil(Obj);
raise;
end;
end
else
Result := nil;
end;
2014-04-22 00:20:00 +02:00
function TDataSetHelper.AsObjectList<T>(CloseAfterScroll: boolean): TObjectList<T>;
var
Objs: TObjectList<T>;
begin
Objs := TObjectList<T>.Create(True);
try
Mapper.DataSetToObjectList<T>(Self, Objs, CloseAfterScroll);
Result := Objs;
except
FreeAndNil(Objs);
raise;
end;
end;
procedure TDataSetHelper.LoadFromJSONArray(AJSONArray: TJSONArray);
begin
2014-04-01 19:36:05 +02:00
Self.DisableControls;
try
Mapper.JSONArrayToDataSet(AJSONArray, Self, false);
finally
Self.EnableControls;
end;
end;
procedure TDataSetHelper.LoadFromJSONArray(AJSONArray: TJSONArray; AIgnoredFields: TArray<string>);
2014-03-24 13:17:30 +01:00
begin
2014-04-01 19:36:05 +02:00
Self.DisableControls;
try
Mapper.JSONArrayToDataSet(AJSONArray, Self, AIgnoredFields, false);
finally
Self.EnableControls;
end;
2014-03-24 13:17:30 +01:00
end;
procedure TDataSetHelper.AppendFromJSONArrayString(AJSONArrayString: string;
AIgnoredFields: TArray<string>);
2014-03-24 13:17:30 +01:00
var
JV: TJSONValue;
begin
JV := TJSONObject.ParseJSONValue(AJSONArrayString);
try
if JV is TJSONArray then
LoadFromJSONArray(TJSONArray(JV), AIgnoredFields)
else
raise Exception.Create('Extected JSONArray in LoadFromJSONArrayString');
finally
JV.Free;
end;
end;
procedure TDataSetHelper.AppendFromJSONArrayString(AJSONArrayString: string);
2014-03-24 13:17:30 +01:00
begin
AppendFromJSONArrayString(AJSONArrayString, TArray<string>.Create());
2014-03-24 13:17:30 +01:00
end;
procedure TDataSetHelper.LoadFromJSONObject(AJSONObject: TJSONObject; AIgnoredFields: TArray<string>);
2014-03-24 13:17:30 +01:00
begin
Mapper.JSONObjectToDataSet(AJSONObject, Self, AIgnoredFields, false);
end;
procedure TDataSetHelper.LoadFromJSONObjectString(AJSONObjectString: string;
AIgnoredFields: TArray<string>);
2014-03-24 13:17:30 +01:00
var
JV: TJSONValue;
begin
JV := TJSONObject.ParseJSONValue(AJSONObjectString);
try
if JV is TJSONObject then
LoadFromJSONObject(TJSONObject(JV), AIgnoredFields)
else
raise Exception.Create('Extected JSONObject in LoadFromJSONObjectString');
finally
JV.Free;
end;
end;
procedure TDataSetHelper.LoadFromJSONObject(AJSONObject: TJSONObject);
begin
LoadFromJSONObject(AJSONObject, TArray<string>.Create());
2014-03-24 13:17:30 +01:00
end;
procedure TDataSetHelper.LoadFromJSONObjectString(AJSONObjectString: string);
2014-03-24 13:17:30 +01:00
begin
LoadFromJSONObjectString(AJSONObjectString, TArray<string>.Create());
end;
2013-10-30 00:48:23 +01:00
end.