+ Start a Discussion
MarceldeBMarceldeB 

Comparing sObjects in list

Hi,

In my apex script I make a copy of an original list with sObjects, to see at a later phase (eg when saving the objects) wether there has been a change of any of the fields by the user.

I use deepclone (clone(true,true)) to make the copy of the relevant objects which I add to the list. Immediately after the cloning, the objects are identical (assertequals of clone and original returns true), after showing them on a vf page with only outputfields they remain identical,

but after showing them in a vf page with inputfields they are seen different (assertequals of clone and original returns false), although all fields (including system fields like lastmodifieddate and id) are identical (assertequals of the individual fields returns true).

 

Anyone who recognizes this and could think of a way to handle this without having to compare each individual field to check for changes?

 

bob_buzzardbob_buzzard

Have you tried using the == operator?  E.g. assert(sobject1==sobject2), rather than the assertEquals?

 

According to the apex docs, == performs a deep check of equality for all fields.  It may be that the assertEquals is using some other equality check, though I can't think what

MarceldeBMarceldeB

Hi, indeed I tried a regular IF(xx == yy) and both assert with == as well as assertequals. Allways all individual fields are the same, but the sObject as a whole is not...

 
bob_buzzardbob_buzzard

I guess the only thing that has happened is that everything has been converted to strings to display on the page and then converted back again when the form has been submitted.   I can't think of anything specific, but I'm wondering about date/time fields and timezones.

 

Have you found any examples where any fields differ?  I know its a pain, but its probably worth taking one object and comparing all of the fields to see where the difference actually occurs.  

MarceldeBMarceldeB

indeed, I have put both records in assert log and compared them in detail on the screen and in an editor, but no differences than the order of the fields listed. but that was also the case when I compared the two objects directly after the clone, when they were still reported as equal.

your idea of fields converted to strings sounds reasonable, but I would still expect that these specific fields would also report unequal in individual field comparisons.

I am thinking that there might be a (hidden) system field which is updated when an sObject is used in a vf screen in input mode. Something with locking or activity logging?

 

bob_buzzardbob_buzzard
Have you tried going through all of the fields and comparing their equality using assertEquals - it might be something that's non-obvious - e.g. the underlying field changes in some way, but when its written out to the log (at which time its converted to a string) is identical.
MarceldeBMarceldeB
indeed, I tried, even with unexpected fields like systemmodstamp and Lastactivitydate, but all fields return to be equal!
bob_buzzardbob_buzzard

Sorry if this is stating the obvious, but have you checked the schema explorer to ensure you are checking all fields on the object.  From your last post it sounds like you have, but I'm clutching at straws here.

 

 

 

 

MarceldeBMarceldeB
Indeed, as far as I can see I checked them all (looked them up in the eclipse schema for the object)...
bob_buzzardbob_buzzard

Bummer.  Shouldn't happen according to the docs - all fields have to be equal for records to be equal.

 

Sorry, can't think of anything else.  All the other ideas I can come up with are based around fields changing, which isn't the case when you check them directly. 

sparkysparky

Hi, I'm experiencing a very similar issue.  Did you ever figure this out?

MarceldeBMarceldeB

No, I created a boolean method comparing the two objects field by field.

sgribisgribi

When you compare new sObjects original/after submit via JSON serialize, you'll notice that the sObject's attributes that do not exist before VF presentation do exist with a null value upon submit of the page.

JSON.serialize(record);

 

Original:   
{"attributes":{"type":"Environment_Component__c"},"Quantity__c":2,"Active__c":true}
Upon Submit:
{"attributes":{"type":"Environment_Component__c"},"Quantity__c":2,"Solution__c":null,"Active__c":true}

 The same issue does not apply for records that were returned by a SOQL query, since the attributes are present before as well as after the VF submission. Itterating over attributes seems to be the only solid approach.

sgribisgribi

I don't know of a better way to dynamically check an object. But here is what I did to meet the need for any sObject:

/* Compare SObjects attributes; ignore non-existent attributes when new value is null */
public static boolean compareSobjects(sObject pBeforeSo, sObject pAfterSo) {
	if(pBeforeSo!=pAfterSo) {
		if(pBeforeSo!=null&&pAfterSo!=null) {
			try {
				map<string, object>beforeMap = (map<string,object>)JSON.deserializeUntyped(JSON.serialize(pBeforeSo));
				map<string, object>afterMap = (map<string,object>)JSON.deserializeUntyped(JSON.serialize(pAfterSo));
				for(string key: afterMap.keySet()) {
					if(beforeMap.containsKey(key)?afterMap.get(key)!=beforeMap.get(key):afterMap.get(key)!=null) {
						if(beforeMap.containsKey(key)) 
							system.debug('compareSobjects: failed - before/after missmatch for key: '+key);
						else
							system.debug('compareSobjects: failed - after is not null for key: '+key);
						return false;
					}
				}
			} catch(Exception e){
				ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.WARNING,'compareSobjects validation error. Please contact a system administrator. Error: '+e.getMessage()));
				return false;
			}
		} else {
			system.debug('compareSobjects: failed - on or more SObjects were null');
			return false;
		}
	} 
	system.debug('compareSobjects: compare success');
	return true;
}

 The compare method will return true if the objects are identical, and false otherwise. Since I am not aware of a way to determine which fields were included in a sObject, the JSON serialize and deserialize are a means to that end.

ramasubramanian.arramasubramanian.ar

To clone the list of sobjects use

List.deepclone(true,true,true);

 

To compare those object I am using this method.

String.valueOf(object1).equals(String.valueOf(object1Clone))