tracker issue : CF-3750732

select a category, or use search below
(searches all categories and all time range)
Title:

Custom serialisers just not fit for purpose

| View in Tracker

Status/Resolution/Reason: To Fix//

Reporter/Name(from Bugbase): Adam Cameron / Adam Cameron (Adam Cameron)

Created: 04/27/2014

Components: Language

Versions: 11.0

Failure Type: Incorrect w/Workaround

Found In Build/Fixed In Build: PublicBeta /

Priority/Frequency: Normal / Some users will encounter

Locale/System: English / Platforms All

Vote Count: 1

There's only a long version for this one:
http://cfmlblog.adamcameron.me/2014/04/coldfusion-11-custom-serialisers-more.html

Extract:
The only way to work here is for each function to have a long array of IF/ELSEIF statements which somehow identify each object type that is serialisable, and then return true from canSerialise(), or in the case of serialize(), go ahead and do the serialisation. So this means this one CFC needs to know about everything which can be serialised in the entire application. Talk about a failure in "separation of concerns".

You know the best way of determining if an object can be seriaslised? Ask it! Don't rely on something else needing to know. This can be achieved very easily in one of two ways:

Check to see if the object implements a "Serializable" interface, which requires a serialize() method to exist.
Or simply take the duck-typing approach: if a CFC implements a serialize() method: it can be serialised. By calling that method. Job done.


Either approach would work fine, keeps things nicely encapsulated, and I see merits in both. And either make far more sense than Adobe's approach. Which is like something from the "OO Failures Special Needs" class.

Deserialisation is trickier. Because it relies on somehow working out how to deserialise() an object. I'm not sure of the best approach here, but - again - how to deserialise something should be as close to the thing needing deserialisation as possible. IE: something in the serialised data itself which can be used to bootstrap the process.

This could simply be a matter of specifying a CFC type at a known place in the serialised data. EG: Adobe stipulates that if the serialised data is JSON, and at the top level of the JSON is a key eg: type, and the value is an extant CFC... use that CFC's deserialize() method. Or it could look for an object which contains a type and a method, or whatever. But Adobe can specify a contract there.

The only place I see a centralised CFC being relevant here is for a mechanism for handling serialised data that is neither a ColdFusion internal type, nor identifiable as above. In this case, perhaps they could provide a mechanism for a serialisation router, which basically has a bunch of routes (if/elseifs if need be) which contains logic as to how to work out how to deserialise the data. But it should not be the actual deserialiser, it should simply have the mechanism to find out how to do it. This is actually pretty much the same in operation as the deserialize() approach in the current implementation, but it doesn't need the canDeserialize() method (it can return false at the end of the routing), and it doesn't need to know about serialising. And also it's not the main mechanism to do the deserialisation, it's just the fall back if the prescribed approach hasn't been used.

TBH, this still sounds a bit jerry-built, and I'm open for better suggestions. This is probably a well-trod subject in other languages, so it might be worth looking at how the likes of Groovy, Ruby or even PHP (eek!) achieve this.

I recommend just binning the current implementation and starting again.

-- 
Adam

----------------------------- Additional Watson Details -----------------------------

Watson Bug ID:	3750732

External Customer Info:
External Company:  
External Customer Name: Adam Cameron.
External Customer Email:  
External Test Config: My Hardware and Environment details:

Attachments:

Comments:

Custom Serializer is just an extension point where you can plugin your own custom serializer. It does not force you to implement in a specific way. If you want the serialization/deserialization logic of a CFC defined in the CFC itself you can do that. i.e., implement serialize/deserialize in the CFC itself. I can write a Person.cfc like, Person.cfc ---------------- <cfcomponent> <cfproperty type="string" name="name"> <cfproperty name="gender" type="string"> <cffunction name="serialize" returntype="string" access="public"> <cfargument name="format" type="string"> <cfif #format# eq "XML"> <!--- we want to do the custom serialization only for Person CFC to XML. ---> <cfsavecontent variable="result"> <cfoutput> <Person> <Name>#this.name#</Name> <Gender>#this.gender#</Gender> </Person> </cfoutput> </cfsavecontent> <cfreturn #result#> <cfelse> <!--- For all other serializations, use coldfusion implementation for serialization. ---> <cfreturn serialize(obj, format)> </cfif> </cffunction> <cffunction name="deserialize" returntype="void" access="public"> <cfargument name="arg" type="string"> <cfset var xmlDoc = xmlParse(arg)> <cfset this.name = xmlSearch(xmlDoc,'//Name')[1].XmlText> <cfset this.gender = xmlSearch(xmlDoc,'//Gender')[1].XmlText> </cffunction> </cfcomponent> TestSerializer.cfc ------------------------- <cfcomponent> <cffunction access="remote" name="canSerialize" returntype="boolean"> <cfargument name="type" type="string"/> <!--- I want to serialize only XML using the custom serializer. ---> <cfif #type# eq "XML"> <cfreturn true> <cfelse> <cfreturn false> </cfif> </cffunction> <cffunction access="remote" name="serialize" returntype="String"> <cfargument name="obj" type="any" hint="The object to be serialized"/> <cfargument name="type" type="string"/> <cfif IsInstanceOf(obj, "Person")> <!--- we want to do the custom serialization only for Person CFC. ---> <cfreturn obj.serialize(type)> <cfelse> <!--- For all other serializations, use coldfusion implementation for serialization. ---> <cfreturn serialize(obj, type)> </cfif> </cffunction> <cffunction name="canDeserialize" access="remote" returntype="boolean"> <cfargument name="type" type="string"/> <!--- I want to deserialize only XML using the custom serializer. ---> <cfif #type# eq "XML"> <cfreturn true> <cfelse> <cfreturn false> </cfif> </cffunction> <cffunction name="deserialize" access="remote" returntype="any"> <cfargument name="arg" type="String" hint="The string to be deserialized"/> <cfargument name="type" type="String" hint="The content-type header of the request."/> <cfif #type# equals "XML" and isXml(arg)> <cfset var xmlDoc = xmlParse(arg)> <!--- I want to parse only the Person XMLs now. ---> <cfif #xmlDoc.XmlRoot.XmlName# equals "Person"> <cfset var result = createObject("component", "Person")> <cfset result.deserialize(arg)> <cfreturn result> <cfelse> <cfreturn deserializeXML(#arg#, true)> </cfif> <cfelse> <cfreturn deserializeXML(#arg#, true)> </cfif> </cffunction> </cfcomponent> Configure the Custom Serializer in your Application.cfc <cfcomponent> <cfset this.customSerializer = "TestSerializer"> </cfcomponent> And try the testcase Testcase.cfm -------------------- <!--- Serializing to XML. ---> <cfset person = new Person()> <cfset person.name = "Paul"> <cfset person.gender = "Male"> <cfset res = serializeXML(person)> <cfdump var="#res#"> <br> <!--- Deserializing the XML to Person ---> <cfset deserializedPerson = deserializeXML(res)> <cfdump var="#deserializedPerson#"> This is perfectly possible using the Custom Serializer feature. This is not a framework. But it is an just an extension point, to plugin your implementation. You can implement the framework as you like it. If you want the serialize/deserialize() functions in the CFC itself, you can do that. Do you want to implement separate CFCs to handle JSON serialization and XML serialization,? You can do that too. That is implementation details. The framework can be a simple CFC handling everything. Or it can be a complex framework, if the situation warrants it.
Comment by Paul N.
12503 | May 06, 2014 07:25:09 AM GMT
You read neither the ticket nor the article, did you, Paul? -- Adam
Comment by External U.
12504 | May 11, 2014 02:09:12 AM GMT
Hello?
Comment by External U.
12505 | January 25, 2016 08:26:06 AM GMT