Skip to content

Instantly share code, notes, and snippets.

@timmaybrown
Last active March 4, 2016 04:12
Embed
What would you like to do?
Here is a component that extends Ben Nadel's JSON Serializer CFC (http://www.bennadel.com/blog/2505-JsonSerializer-cfc-A-Data-Serialization-Utility-For-ColdFusion.htm) and uses the jsonUTIL project in lieu of native CF serialization to fix some known issues. This approach uses a CFDirectory call to find all my persistent CFCs (in this case they …
<cfcomponent extends="model.util.jsonSerializer" singleton="true">
<cfproperty name="jsonUTIL" inject="model" />
<cffunction name="init">
<!---// let's init Mr. Nadel's serializer--->
<cfset super.init()>
<!--- FOR Reference: PROPERTIES THAT ARE AVAILABLE due to the SUPER.init() call above
// Every key is added to the full key list.
fullKeyList = {};
// These key lists determine special data serialization.
booleanKeyList = {};
integerKeyList = {};
floatKeyList = {};
dateKeyList = {};
// These keys will NOT be used in serialization (ie. the key/value pairs will not be added
// to the serialized output).
blockedKeyList = {};
// When serializing values, ColdFusion has a tendency to convert strings to numbers if those
// strings look like numbers. We will prepend the string values with this "conversion blocker"
// so that we can force ColdFusion to make a string (this gets removed after serialization).
START_OF_STRING = chr( 2 );
--->
<!--- PERSISTENT ENTITY SEARCH LOGIC --->
<cfset var qEntities = "">
<cfset var qModels = "">
<!--- LET's fetch our model's this be an array of persitent cfc names,
but I don't want to have to update the array for every new entity I add to my model's folder --->
<cfdirectory
action="list"
type="file"
listinfo="name"
directory="#GetDirectoryFromPath( GetBaseTemplatePath() )#model\" name="qModels" />
<cfquery name="qEntities" dbtype="query">
SELECT Name FROM qModels WHERE Name NOT LIKE '%Service.cfc'
AND Name NOT IN ('DummyFlash.cfc','ORMEventHandler.cfc')
</cfquery>
<cfset var arEntities = listToArray(valueList(qEntities.Name))>
<!--- /END PERSISTENT ENTITY SEARCH LOGIC --->
<!---
UNCOMMENT IF YOU WANT TO GET ALL avail type Methods
<cfset pMeths = GetComponentMetaData('jsonSerializer').functions>
<cfset f = "">
<cfloop array="#pMeths#" index="p">
<cfif !listFindnoCase(f, p.name) && left(p.name, 2) EQ 'as'>
<cfset f = listAppend(f, p.name)>
</cfif>
</cfloop>
<cfdump var="#f#"><cfabort>
--->
<!--- COMPONENT METADATA LOGIC --->
<cfscript>
var meta = {};
var md = {};
// iteration to fetch all entity properties if the cfc is a persistent cfc
for(var e=1; e <= arrayLen(arEntities); e++){
c = listFirst(arEntities[e], '.');
md = GetComponentMetaData("model.#c#");
if (structKeyExists(md,'persistent')) meta[c] = md.properties;
}
// now let's prep the struct of property names the value of the property names are the method name available in the serializer
// eg: toInit['userID'] = 'asInteger';
// this will allow for dyanmic method invocation of the asInteger method now available from the inherited serailizer
var toInit = {};
var entityName = "";
var entityProperties = [];
for(entityName in meta){
entityProperties = meta[entityName];
for(var p=1; p <=arrayLen(entityProperties); p++){
toInit[entityProperties[p].name] = (structKeyExists(entityProperties[p], 'ormType')) ? getSerializeMethByORMType(entityProperties[p].ormType) : "asAny";
}
}
//now that the toInit structure is ready lets dynamically invoke the methods.
// Thanks to Adam Cameron for this blogging about this little trick (http://cfmlblog.adamcameron.me/2012/09/functions-and-their-execution-context.html)
for(propertyName in toInit) {
this.theMethod = this[toInit[propertyName]]; //put a reference to the ACTUAL method into this static property name
this.theMethod(key=propertyName); // now invoke the dynamic method
}
// now for anything else that wasn't a proper name that was custom just continue to chain calls like Mr Nadel shows in his blog post
this
.asInteger("adCount")
.asString("partnerName")
.asString("configStatus")
;
</cfscript>
<cfreturn this>
</cffunction>
<!------------------------------------------------------------------------
PUBLIC METHODS
------------------------------------------------------------------------->
<!--- override Mr. Nadel's serialize method and use injected jsonUTIL service in lieu of CF's serializeJSON() --->
<cffunction name="serialize" access="public" returntype="String">
<cfargument name="input" required="true" type="any" />
<cfscript>
var preparedInput = prepareInputForSerialization( input );
var serializedInput = jsonUtil.serialize(var=preparedInput, strict="true");
// At this point, we have the serialized response; but, the response contains unwanted
// artifacts that were required to enforce string value integrity. Those must now be
// removed, post-serialization.
var sanitizedResponse = removeStartOfStringMarkers( serializedInput );
return( sanitizedResponse );
</cfscript>
</cffunction>
<!------------------------------------------------------------------------
PRIVATE METHODS
------------------------------------------------------------------------->
<cffunction name="getSerializeMethByORMType" access="private">
<cfargument name="ormType" type="string" required="true">
<cfscript>
var stORMTypes = {
"big_decimal" = "asFloat"
,"binary" = "asString"
,"blob" = "asString"
,"boolean" = "asBoolean"
,"char" = "asString"
,"character" = "asString"
,"clob" = "asString"
,"date" = "asDate"
,"double" = "asFloat"
,"float" = "asFloat"
,"int" = "asInteger"
,"integer" = "asInteger"
,"long" = "asString"
,"serializable" = "asString"
,"short" = "asString"
,"string" = "asString"
,"text" = "asString"
,"timestamp" = "asDate"
,"true_false" = "asBoolean"
,"yes_no" = "asBoolean"
};
return (structKeyExists(stORMTypes, ormType)) ? stORMTypes[ormType] : "asAny";
</cfscript>
</cffunction>
</cfcomponent>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment