Looking for a way in Delphi to do deep object comparison for me, preferably 2010 RTTI based as my objects don't inherit from TComponent
. I'm developing a test framework in DUnit
and need something solid which will point out exactly which field is causing problems (serialization comparison leaves it a bit vague).
Sort of solved this myself, implemented as a class helper for TObject so can be used everywhere if people want it. D2010 and up due to RTTI but you may be able to convert it to use original RTTI stuff.
Code below may be buggy as originally mine was for DUnit and had lots of checks in it instead of changing the result and doesn't support TCollections or a load of other special cases but can be adapted for that by using the if-elseif-then switch in the middle.
If you have any suggestions and additions please don't hesitate to comment so I can add them to it so other people can use this.
Have fun coding
Barry
unit TObjectHelpers;
interface
uses classes, rtti;
type
TObjectHelpers = class Helper for TObject
function DeepEquals (const aObject : TObject) : boolean;
end;
implementation
uses sysutils, typinfo;
{ TObjectHelpers }
function TObjectHelpers.DeepEquals(const aObject: TObject): boolean;
var
c : TRttiContext;
t : TRttiType;
p : TRttiProperty;
begin
result := true;
if self = aObject then
exit; // Equal as same pointer
if (self = nil) and (aObject = nil) then
exit; // equal as both non instanced
if (self = nil) and (aObject <> nil) then
begin
result := false;
exit; // one nil other non nil fail
end;
if (self <> nil) and (aObject = nil) then
begin
result := false;
exit; // one nil other non nil fail
end;
if self.ClassType <> aObject.ClassType then
begin
result := false;
exit;
end;
c := TRttiContext.Create;
try
t := c.GetType(aObject.ClassType);
for p in t.GetProperties do
begin
if ((p.GetValue(self).IsObject)) then
begin
if not TObject(p.GetValue(self).AsObject).DeepEquals(TObject(p.GetValue(aObject).AsObject)) then
begin
result := false;
exit;
end;
end
else if AnsiSameText(p.PropertyType.Name, 'DateTime') or AnsiSameText(p.PropertyType.Name, 'TDateTime') then
begin
if p.GetValue(self).AsExtended <> p.GetValue(aObject).AsExtended then
begin
result := false;
exit;
end;
end
else if AnsiSameText(p.PropertyType.Name, 'Boolean') then
begin
if p.GetValue(self).AsBoolean <> p.GetValue(aObject).AsBoolean then
begin
result := false;
exit;
end;
end
else if AnsiSameText(p.PropertyType.Name, 'Currency') then
begin
if p.GetValue(self).AsExtended <> p.GetValue(aObject).AsExtended then
begin
result := false;
exit;
end;
end
else if p.PropertyType.TypeKind = tkInteger then
begin
if p.GetValue(self).AsInteger <> p.GetValue(aObject).AsInteger then
begin
result := false;
exit;
end;
end
else if p.PropertyType.TypeKind = tkInt64 then
begin
if p.GetValue(self).AsInt64 <> p.GetValue(aObject).AsInt64 then
begin
result := false;
exit;
end;
end
else if p.PropertyType.TypeKind = tkEnumeration then
begin
if p.GetValue(self).AsOrdinal <> p.GetValue(aObject).AsOrdinal then
begin
result := false;
exit;
end;
end
else
begin
if p.GetValue(self).AsVariant <> p.GetValue(aObject).AsVariant then
begin
result := false;
exit;
end;
end;
end;
finally
c.Free;
end;
end;
end.
Consider using OmniXML persistence.
For XML differencing, I have written a utility using OmniXML that will do an XML diff, and there are many XML comparison tools out there.
I used OmniXML to do an XML differencing tool for exactly this purpose, and it worked great for me. Unfortunately that tool contains many domain specific things and is closed-source and belongs to a former employer so I cannot post the code.
My comparision tool had a simple algorithm:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With