Hello Wagner,
I have a global function that allows me to dynamically set values in my objects.
class function TDBTools.Patch<T>( AEntity: T; AJson: TJSONObject): T;
var
i: Integer;
begin
for i := 0 to AJson.Count do
TsngRtti.SetValue( AEntity, 'F'+AJson.Pairs[i].JsonString.Value, AJson.Pairs[i].JsonValue);
Result := AEntity;
end;
class function TsngRtti.SetValue( AInstance: TObject; AFieldname: String; AFieldValue: TJSONValue): Boolean;
var
LField: TRttiField;
LValue: TValue;
begin
Result := False;
if AInstance.TryGetField( AFieldname, LField) then begin
LValue := ConvertJSONValueToValue( AFieldValue, LField.FieldType);
if not LValue.IsEmpty then
LField.SetValue( AInstance, LValue);
end;
end;
The ConvertJSONValueToValue
function is my own function where I convert JSON to TValue.
But now I have some strings that are nullable.
And nullable doesn't work with TRttiField.SetValue.
Last line: LField.SetValue( AInstance, LValue);
What can I do?
As short and simple as possible.
Thomas
P.S: In the ConvertJSONValueToValue
function I can make case distinctions and recognize the Nullable type. But what happens next?
I have a solution:
class function TsngRtti.SetValue( AInstance: TObject; AFieldname: String; AFieldValue: TJSONValue): Boolean;
var
LField: TRttiField;
LValue: TValue;
NewData: Nullable<String>;
begin
Result := False;
if AInstance.TryGetField( AFieldname, LField) then begin
if copy( LField.FieldType.Name, 1, 8) = 'Nullable' then
Result := SetNullableValue( AInstance, LField, AFieldValue)
else begin
LValue := ConvertJSONValueToValue( AFieldValue, LField.FieldType);
Result := not LValue.IsEmpty;
if Result then
LField.SetValue( AInstance, LValue);
end;
end;
end;
and
resourcestring
_Nullable_string = 'Nullable<System.string>';
_Nullable_Integer = 'Nullable<System.Integer>';
_Nullable_TDateTime = 'Nullable<System.TDateTime>';
_Nullable_TGUID = 'Nullable<System.TGUID>';
_Nullable_Boolean = 'Nullable<System.Boolean>';
_Nullable_Double = 'Nullable<System.Double>';
_Nullable_Int64 = 'Nullable<System.Int64>';
class function TsngRtti.SetNullableValue( AInstance: TObject; AField: TRttiField; AFieldValue: TJSONValue): Boolean;
var
LValue: TValue;
LName: String;
LNewString : Nullable<String>;
LNewTGuid : Nullable<TGUID>;
LNewInteger : Nullable<Integer>;
LNewTDateTime : Nullable<TDateTime>;
LNewDouble : Nullable<Double>;
LNewInt64 : Nullable<Int64>;
LNewBoolean : Nullable<Boolean>;
begin
Result := False;
LValue := AField.GetValue(AInstance);
LName := AField.FieldType.Name;
if LName = _Nullable_string then begin
// String
LNewString := LValue.AsType<Nullable<String>>;
LNewString.Value := TJSONString( AFieldValue).Value;
TValue.Make( @LNewString, TypeInfo( Nullable<String>), LValue);
end else if LName = _Nullable_Integer then begin
// Integer
LNewInteger := LValue.AsType<Nullable<Integer>>;
LNewInteger.Value := TJSONNumber( AFieldValue).AsInt;
TValue.Make( @LNewInteger, TypeInfo(Nullable<Integer>), LValue);
end else if LName = _Nullable_TDateTime then begin
// TDateTime
LNewTDateTime := LValue.AsType<Nullable<TDateTime>>;
LNewTDateTime.Value := ISODateToDateTime( TJSONString( AFieldValue).Value);
TValue.Make( @LNewTDateTime, TypeInfo(Nullable<TDateTime>), LValue);
end else if LName = _Nullable_TGUID then begin
// TGUID
LNewTGUID := LValue.AsType<Nullable<TGUID>>;
LNewTGUID.Value := DirtyStringToGuid( TJSONString( AFieldValue).Value);
TValue.Make( @LNewTGUID, TypeInfo(Nullable<TGUID>), LValue);
end else if LName = _Nullable_Boolean then begin
// Boolean
LNewBoolean := LValue.AsType<Nullable<Boolean>>;
LNewBoolean.Value := TJSONBool( AFieldValue).AsBoolean;
TValue.Make( @LNewBoolean, TypeInfo(Nullable<Boolean>), LValue);
end else if LName = _Nullable_Double then begin
// Double
LNewDouble := LValue.AsType<Nullable<Double>>;
LNewDouble.Value := TJSONNumber( AFieldValue).AsDouble;
TValue.Make( @LNewDouble, TypeInfo(Nullable<Double>), LValue);
end else if LName = _Nullable_Int64 then begin
// Int64
LNewInt64 := LValue.AsType<Nullable<Int64>>;
LNewInt64.Value := TJSONNumber( AFieldValue).AsInt64;
TValue.Make( @LNewInt64, TypeInfo(Nullable<Int64>), LValue);
end else
Exit;
AField.SetValue( AInstance, LValue);
Result := True;
end;
If anyone has a better solution, please write here.
For anyone interested, here is my call in a XData service function:
// read JSON Entity
LJson := // Get data from service func parameter
// read DB Entity
LEntity := ObjectManager.Find<TMyEntity>( { The ID });
// patch Data
TDBTools.Patch<TMyEntity>( LEntity, LJson);
ObjectManager.Update( LEntity);
ObjectManager.Flush;
// Warning: foreign keys, references must be handled separately
1 Like