Is the JSON Serialization of an Apex Object deterministic (repeatable?)

apexjson

Use Case: In an after update trigger, I need to check a long-ish list of fields for changes, to decide whether to flag the record for re-export to an external system. I already have a wrapper object used to export the SObject (opportunity), which constructs its internal state from only the fields I care about. So the simplest way to check to check all of the fields on the records would be to construct a wrapper for the Trigger.new and Trigger.old version of each record, and compare the wrappers by value. Apex doesn't have equality-by-value comparison out of the box, but comparing the JSON-serialized strings is trivial, if the serialization order is deterministic. Note that I don't care if the serialization changes between releases; I'm only comparing within the bounds of a single Apex transaction.

I am aware that I could alternatively write a compare method for the wrappers which checks each field, but with a list of ~40 fields, I'd rather not. I could also write a method which compares the 2 SObjects using a list of field names and SObject.get(), which is probably my fallback position, but it still means maintaining both the wrapper class constructor logic AND a list of SObject fields if the list of fields changes. Comparing the wrappers by value seems to me the simplest method conceptually, but I couldn't find anything documented regarding repeatability of JSON Serialization. I've checked the Apex Docs, Apex Ref, the Canonical JSON Q&A, and searched SFSE and the web. I'm guessing there may not be an officially documented answer, but is there any reason to believe it is NOT deterministic?

Edit to add: my Apex wrapper object has only simple Apex types for properties; no SObjects or other Apex objects are contained in the wrapper.

Best Answer

This is not documented so you shouldn't rely on it being consistent - Salesforce would be at liberty to change the way it works since JSON properties are not inherently ordered.

As a slight aside, note that JSON serialization of SObjects seems to sort lexicographically (though in some testing I previously did the behaviour seemed different).