G'day:
There's a new feature in ColdFusion 11:
Currently, the cases for struct keys are not preserved in ColdFusion. The struct keys get converted to upper case automatically.
[...]
To enable case preservation of struct keys at the application level, modify the application.cfc file by setting:
this .serialization.preservecaseforstructkey = true
|
On a whim, I decided to check how well this had been implemented...
The first problem with this is the setting name:
this.serialization.preservecaseforstructkey
. This is not to do with serialisation... it's simply to do with preserving key case in structs. That the chief use of this is going to be when an object is serialised is neither really here nor there. So it needs a new name. I suggest just ditching the reference to serialisation. Or stick it in a substruct called "codeSettings" or something.
Additionally, I can't help but think this should be the
default behaviour, and if - for some weirdo reason - one wanted it off,
then one should use the config setting to do so. In the normal course of events, why would one simply not just want to preserve variable name case?
Secondly... wouldn't this be more a compiler setting than an application setting?
Thirdly... does it work? Here's some code that tests some options:
server.lowercase = true;
server.UPPERCASE = true;
server.mixedCase = true;
json = serializeJson(server);
cf = deserializeJson(json);
writeDump(var=[{original=server},{json=json},{deserialiised=cf}], label="server scope");
writeOutput("<hr>");
application.lowercase = true;
application.UPPERCASE = true;
application.mixedCase = true;
json = serializeJson(application);
cf = deserializeJson(json);
writeDump(var=[{original=application},{json=json},{deserialiised=cf}], label="application scope");
writeOutput("<hr>");
session.lowercase = true;
session.UPPERCASE = true;
session.mixedCase = true;
json = serializeJson(session);
cf = deserializeJson(json);
writeDump(var=[{original=session},{json=json},{deserialiised=cf}], label="session scope");
writeOutput("<hr>");
request.lowercase = true;
request.UPPERCASE = true;
request.mixedCase = true;
json = serializeJson(request);
cf = deserializeJson(json);
writeDump(var=[{original=request},{json=json},{deserialiised=cf}], label="request scope");
writeOutput("<hr>");
structClear(variables);
variables.lowercase = true;
variables.UPPERCASE = true;
variables.mixedCase = true;
json = serializeJson(variables);
cf = deserializeJson(json);
writeDump(var=[{original=variables},{json=json},{deserialiised=cf}], label="variables scope");
writeOutput("<hr>");
dotNotation = {};
dotNotation.lowercase = true;
dotNotation.UPPERCASE = true;
dotNotation.mixedCase = true;
json = serializeJson(dotNotation);
cf = deserializeJson(json);
writeDump(var=[{original=dotNotation},{json=json},{deserialiised=cf}], label="struct with dot notation");
writeOutput("<hr>");
literalNotation = {
lowercase = true,
UPPERCASE = true,
mixedCase = true
};
json = serializeJson(literalNotation);
cf = deserializeJson(json);
writeDump(var=[{original=literalNotation},{json=json},{deserialiised=cf}], label="struct with literal notation");
writeOutput("<hr>");
viaStructInsert = structNew();
structInsert(viaStructInsert, "lowercase", true);
structInsert(viaStructInsert, "UPPERCASE", true);
structInsert(viaStructInsert, "mixedCase", true);
json = serializeJson(viaStructInsert);
cf = deserializeJson(json);
writeDump(var=[{original=viaStructInsert},{json=json},{deserialiised=cf}], label="struct via structInsert()");
writeOutput("<hr>");
Here I put some case-significant keys into various scopes:
server,
application,
session,
request,
variables; as well as into structs using various syntax options:
dot notation,
struct-literal notation, and using
structInsert().
And the results:
server scope - array |
1 |
server scope - struct |
original |
server scope - struct |
UPPERCASE | true |
coldfusion |
server scope - struct |
InstallKit | Native Windows |
appserver | Tomcat |
expiration | {ts '2014-05-15 09:40:59'} |
productlevel | Evaluation |
productname | ColdFusion Server |
productversion | 11,0,0,288464 |
rootdir | C:\apps\adobe\ColdFusion\11beta\gettingstarted\cfusion |
supportedlocales |
|
lowercase | true |
mixedCase | true |
os |
server scope - struct |
additionalinformation | [empty string] |
arch | amd64 |
buildnumber | [empty string] |
name | Windows 8 |
version | 6.2 |
|
|
|
2 |
server scope - struct |
json | {
"mixedCase":true,
"lowercase":true,
"UPPERCASE":true
} |
|
3 |
server scope - struct |
deserialiised |
server scope - struct |
UPPERCASE | YES |
coldfusion |
server scope - struct |
InstallKit | Native Windows |
appserver | Tomcat |
expiration | May, 15 2014 09:40:59 |
productlevel | Evaluation |
productname | ColdFusion Server |
productversion | 11,0,0,288464 |
rootdir | C:\apps\adobe\ColdFusion\11beta\gettingstarted\cfusion |
supportedlocales |
|
lowercase | YES |
mixedCase | YES |
os |
server scope - struct |
additionalinformation | [empty string] |
arch | amd64 |
buildnumber | [empty string] |
name | Windows 8 |
version | 6.2 |
|
|
|
application scope - array |
1 |
application scope - struct |
original |
application scope - struct |
applicationname | testKeyCase01 |
lowercase | true |
mixedcase | true |
uppercase | true |
|
|
2 |
application scope - struct |
json | {"mixedcase":true,"uppercase":true,"lowercase":true,"applicationname":"testKeyCase01"} |
|
3 |
application scope - struct |
deserialiised |
application scope - struct |
applicationname | testKeyCase01 |
lowercase | YES |
mixedcase | YES |
uppercase | YES |
|
|
session scope - array |
1 |
session scope - struct |
original |
session scope - struct |
cfid | 118 |
cftoken | d6d97e96918f566f-9A809602-EB3B-ADF4-7353897DF8912428 |
lowercase | true |
mixedcase | true |
sessionid | TESTKEYCASE01_118_d6d97e96918f566f-9A809602-EB3B-ADF4-7353897DF8912428 |
uppercase | true |
urltoken | CFID=118&CFTOKEN=d6d97e96918f566f-9A809602-EB3B-ADF4-7353897DF8912428 |
|
|
2 |
session scope - struct |
json | {"mixedcase":true,"uppercase":true,"urltoken":"CFID=118&CFTOKEN=d6d97e96918f566f-9A809602-EB3B-ADF4-7353897DF8912428","cftoken":"d6d97e96918f566f-9A809602-EB3B-ADF4-7353897DF8912428","lowercase":true,"cfid":118,"sessionid":"TESTKEYCASE01_118_d6d97e96918f566f-9A809602-EB3B-ADF4-7353897DF8912428"} |
|
3 |
session scope - struct |
deserialiised |
session scope - struct |
cfid | 118 |
cftoken | d6d97e96918f566f-9A809602-EB3B-ADF4-7353897DF8912428 |
lowercase | YES |
mixedcase | YES |
sessionid | TESTKEYCASE01_118_d6d97e96918f566f-9A809602-EB3B-ADF4-7353897DF8912428 |
uppercase | YES |
urltoken | CFID=118&CFTOKEN=d6d97e96918f566f-9A809602-EB3B-ADF4-7353897DF8912428 |
|
|
request scope - array |
1 |
request scope - struct |
original |
request scope - struct |
lowercase | true |
mixedcase | true |
uppercase | true |
|
|
2 |
request scope - struct |
json | {"uppercase":true,"lowercase":true,"mixedcase":true} |
|
3 |
request scope - struct |
deserialiised |
request scope - struct |
lowercase | YES |
mixedcase | YES |
uppercase | YES |
|
|
variables scope - array |
1 |
variables scope - struct |
original |
variables scope - struct |
CF |
variables scope - struct |
UPPERCASE | YES |
lowercase | YES |
mixedCase | YES |
|
JSON | {"lowercase":true,"UPPERCASE":true,"mixedCase":true} |
UPPERCASE | true |
lowercase | true |
mixedCase | true |
|
|
2 |
variables scope - struct |
json | {"lowercase":true,"UPPERCASE":true,"mixedCase":true} |
|
3 |
variables scope - struct |
deserialiised |
variables scope - struct |
UPPERCASE | YES |
lowercase | YES |
mixedCase | YES |
|
|
struct with dot notation - array |
1 |
struct with dot notation - struct |
original |
struct with dot notation - struct |
UPPERCASE | true |
lowercase | true |
mixedCase | true |
|
|
2 |
struct with dot notation - struct |
json | {"mixedCase":true,"UPPERCASE":true,"lowercase":true} |
|
3 |
struct with dot notation - struct |
deserialiised |
struct with dot notation - struct |
UPPERCASE | YES |
lowercase | YES |
mixedCase | YES |
|
|
struct with literal notation - array |
1 |
struct with literal notation - struct |
original |
struct with literal notation - struct |
UPPERCASE | true |
lowercase | true |
mixedCase | true |
|
|
2 |
struct with literal notation - struct |
json | {"mixedCase":true,"UPPERCASE":true,"lowercase":true} |
|
3 |
struct with literal notation - struct |
deserialiised |
struct with literal notation - struct |
UPPERCASE | YES |
lowercase | YES |
mixedCase | YES |
|
|
struct via structInsert() - array |
1 |
struct via structInsert() - struct |
original |
struct via structInsert() - struct |
UPPERCASE | true |
lowercase | true |
mixedCase | true |
|
|
2 |
struct via structInsert() - struct |
json | {"mixedCase":true,"UPPERCASE":true,"lowercase":true} |
|
3 |
struct via structInsert() - struct |
deserialiised |
struct via structInsert() - struct |
UPPERCASE | YES |
lowercase | YES |
mixedCase | YES |
|
|
The results aren't bad, but aren't perfect either. With basic structs, everything works OK. However with some of the scopes, it's not working properly: the application, session and request scopes do not preserve key case still.
To be honest, this is a slightly contrived test, but Adobe should just do the job properly and thoroughly.
Incidentally: Railo already has this functionality, and it works for everything except the request scope:
request scope |
1 |
original |
lowercase |
|
mixedcase |
|
uppercase |
|
|
|
2 |
json |
string | {"uppercase":true,"lowercase":true,"mixedcase":true} |
|
|
3 |
deserialised |
lowercase |
|
mixedcase |
|
uppercase |
|
|
|
|
So... it's good that they've decided to do this - and about time! - but there's still a bit of work to do.
Here's an issue though. What Adobe is trying to deal with here is that in other systems, object names can be case-sensitive. Most significantly, JavaScript is. I think Adobe have dealt with half the issue here, but have not dealt with the whole issue. Consider this code:
// inbound.cfm
json = '{"a":"a","A":"A"}';
cfml = deserializeJSON(json);
writeDump([{json=json},{cfml=cfml}]);
Here the key names are case-sensitive... and... other than case, the key names are the same. CFML still does not deal with this:
array |
1 |
struct |
json | {"a":"a","A":"A"} |
|
2 |
|
This is predictable, and is different from "case preservation". But isn't this still part of what Adobe are trying to deal with here?
Also, there is data loss there: part of the inbound data simply vanishes without any notification from ColdFusion. I think this is probably not a good thing.
That said, it's also an edge-case, so I'm gonna file that one under "food for thought", not "it's an issue".
I can't help but think though that if Adobe are gong down this route of changing CF to pay attention to casing issues... maybe it
should do a complete job of it? Obviously as a toggle-able setting though.
Oh... and for the sake of completeness I tested with WDDX as well, and it worked fine.
Bottom line:
- rename the config setting (3712134);
- fix the application, session and request scopes to do the job properly (3712135).
--
Adam