preserveCaseForStructKey Doesn't Work Inside Application.cfc In Adobe ColdFusion 2021
Over the New Year's holiday, I ran into a rather peculiar behavior regarding the preservation of key-casing and the serializeJson()
function in Adobe ColdFusion 2021. It appears that the serialization setting for preserveCaseForStructKey
doesn't apply to code that resized physically within the Application.cfc
life-cycle event handlers. To demonstrate this, we can setup a simple demo in which we serialize data across the event handlers and then dump-out the response:
component
output = false
hint = "I define the application settings and event handlers."
{
this.name = "JsonKeyTest";
this.serialization = {
preserveCaseForStructKey: true,
preserveCaseForQueryColumn: true
};
// ---
// LIFE-CYCLE METHODS.
// ---
public void function onRequestStart() {
request.queue = [
serializeJson({
place: "onRequestStart()",
hello: "world"
})
];
}
public void function onRequest() {
request.queue.append(
serializeJson({
place: "onRequest()",
hello: "world"
})
);
include "./index.cfm";
}
public void function onRequestEnd() {
request.queue.append(
serializeJson({
place: "onRequestEnd()",
hello: "world"
})
);
writeDump( request.queue );
}
}
In this Application.cfc
, the request.queue
Array is just aggregating the result of several serializeJson()
calls. The calls are all exactly the same; but, they have different place
values so that we can see what is what. Notice that my onRequest()
event-handler explicitly pulls-in index.cfm
, which has its own serializeJson()
call:
<cfscript>
request.queue.append(
serializeJson({
place: "index.cfm",
hello: "world"
})
);
</cfscript>
Now, when we run this in Adobe ColdFusion 2021, we get the following output:
As you can see from the output, the place
and hello
struct-keys maintain proper key-casing within the index.cfm
only - all serialization performed directly within the bounds of the Application.cfc
component file converted the keys to all uppercase.
I'm going to consider this a bug; and, I'll look in the Adobe ColdFusion bug tracker to see if there is already a ticket. To get around this, I just had to quote the keys within my implicit struct creation - a technique that has been in ColdFusion since forever.
Epilogue: ColdFusion CFIDE Admin Settings
Inside the Adobe ColdFusion Administrator, there is a server settings option, "Preserve case for Struct keys for Serialization". I did not test with this option enabled because any attempt to update this setting inside a CommandBox container leads to the following error:
In memory file system limit cannot be less than Application limit.
I don't know if this is a bug in the CommandBox setup; or, a bug in the underlying ColdFusion setup. But, any attempt to change this leads to other ColdFusion errors that are even less visible.
UPDATE: Production Test
I just ran a test on my production Adobe ColdFusion 2021 server (ie, this blog), and I cannot reproduce the behavior. However, when I look in the ColdFusion Administrator, the "Preserve case for Struct keys for Serialization" server setting (discussed above) is checked. So, perhaps this checkbox allows the setting to work within the Application.cfc
file.
Want to use code from this post? Check out the license.
Reader Comments
Hey, Ben, I wonder if the variations you see are an indication that you are being bit by this instead, from the docs on serializejson (where this setting's feature is most likely to be observed). Speaking of both the app-level setting and admin setting:
"Note that this setting is used during compilation of the CFML page and therefore if this flag is changed (in the administrator or programmatically), any pages relying on the change must be recompiled. This is done typically by simply editing the file (make any change at all) and re-executing it. If "trusted cache" is enabled in the ColdFusion Administrator, you must clear the template cache (of at least those affected files), which can also be done from within the ColdFusion Administrator Caching page."
I'm just about to start a client call so I can't take even a minute to confirm this myself. Just wanted to put it out there for you or others to confirm.
Consider also that it notes:
"Key preservation do not work on keys in application, session, or request scopes."
HTH
@Charlie,
Hmmmm, very curious. I don't have that Docker container running at the moment; but, after work, I'll try to flush all the caches and see if it makes any difference. That said, when I switched from unquoted keys to quoted keys, the serialization worked as you would expect, which should indicate that the template was recompiled in order for those changes to take place. Switch back to unquoted keys, however, brought the undesired behavior back to the surface.
That said, I'll try flushing the caches after work. Also, I'm shocked that the doc page on
serializeJson()
is sooooo freaking long 😜 who knew you'd have to think so much about serialization.@Ben,
Ah, ok. If you changed it back to NOT quoted and the problem returned, then it's of course NOT about this need of recompilation. :-)
It was because you DID find the change in behavior on your first changing it to quotes that I wondered if perhaps it was this issue--since doing that would of course force recompilation (unless trusted cache was on). But sure, if changing it back to not quoted returns the behavior immediately, then that's not the problem, and so this is issue observe is one worth noting (and filing a bug report).
Even so, perhaps readers will still benefit from hearing this news about the need of compilation of any affected code (after changing the setting in the admin or app settings). It could easily confuse folks, leading them to think the feature "doesn't work". And I was surprised myself to see the indication that it didn't apply to those 3 scopes.
And yep, just like that serializejson page, it's quite amazing how deep are the depths of things one can know and learn about CF/CFML. The fact that you and I (and others) continue to blog about our discovery of things--and have done so for so many years--is surely an indication of that.
And yet so many do work day to day getting things done without knowing even 1% of its capabilities. Ah, the two-edged sword of knowledge: one can know "just enough to get the job done", while also knowing "just enough to be dangerous". :-)
@Charlie,
I'll be honest, the whole per-app-settings stuff always feels like magic to me. I don't quite understand - and I'm scared to think too deeply about it - how something that is loading per page-request (
Application.cfc
) can dynamically affect things are persisted across page requests. The fact thatthis.mappings
even works at all kind of blows my mind.Don't get me wrong - I 10000% love the fact that I can apply settings in the application itself and not have to worry about getting into the ColdFusion admin; but, it sometimes feels like tweaking the nuts-and-bolts on a race-care while it's driving 🤪
@Ben,
Fair point, Ben. And tbh there are aspects of using those app settings which CAN get confusing, regarding how changing them may impact things. I've often wondered/wished to see documented if any aspects get loaded in such a way that the instantiation of the application cfc in memory doesn't pick up a change until the app is reloaded. (And perhaps like you, I'm referring mostly here to code in the pseudo-constructor--that code that is NOT in any specific method inside the application.cfc.)
But in the big picture, we should regard these app-level settings (in the this scope of application.cfc) as being simply a way to override what may (or may not) be set at the admin level, like with mappings. Most of the time, that's really all we need to regard. It's when a value may change (whether by changing the file, or by using a variable) that things might get more subtle.
@Charlie,
The thing that makes me the most uncomfortable is that some of the stuff I need in the per-app settings is actually being read out of a JSON file (this blog isn't sophisticated enough to use ENV variables ... yet). And, I don't want the overhead of reading a file on every single page request. So, I actually cache it in the
server
scope and then pull it out of there on each page request. Something like (pseudo code):Maybe it's a "premature optimization", but my gut just tells me that you don't want concurrent requests battling for File IO.
Ok, I'm done venting about my app.cfc insecurities :D
@Ben,
Oh, for sure, such optimization is wise. Sometimes people are woefully unaware of potentially significant overhead in just their application.cfc/cfm processing (which can be all the more easy to miss when calling still other CFCs there, or using includes files or custom tags).
But to be clear, I just was referring to the aspect of the defined application-level settings. I assumed you were talking about those in your previous comment ("magic"), and it's that which I was agreeing could be perhaps still more mysterious than many know. :-)
@Charlie,
Yes, same stuff. When I set up things like
this.smtpServersettings
, I'm pulling that out of a private config file.... on every request. I should look up to see if there's maybe just a way where I can load that config into an ENV or something and then not have to worry about this. Maybe that's an IIS setting somewhere.@Ben,
Yep, sure, there would be value in avoiding i/o in loading the content on each request, whether for the server variable you were discussing before or these app settings. As for many possible options to do that (from env vars to caches to shared scopes, and more), there are pros and cons each.
As for your last point though, about "an IIS setting", I wonder if that's really what you had meant to say. I don't readily see a connection between this discussion and IIS--even if one is using IIS. :-) But if you see one and care to elaborate, I'm sure some would be interested to hear.
But really all this is getting pretty far afield of your original post. Again, my first comment was more about seeing if perhaps your issue there was related to that odd but documented behavior I shared. As it's not, I don't mean to be dragging us away from that original point. Since you're offering thoughts, I'm happy to respond, if it may be helpful. But I'll understand if you may opt to let this part of the discussion go at this point. :-)
@Charlie,
Ha ha, I'm never afraid of a good tangent. Re: the "IIS setting", my thought there was something akin to a
.env
file that will automatically get "slurred up" in the server context in various app servers (for example, I think Node.js may use something like this with some plugins). So, imagine that I could tell my IIS site to read a given file and store the values int the environment upon starting. Then, in myApplication.cfc
file, I could just pull though out of the environment variables rather than trying to read them from disk.Though, maybe this is where my low-level server stuff is failing me. Because, I do know that the ColdFusion app server is actually different from the IIS internet publishing service; and, that CF is likely already running at the time the IIS site starts up. So maybe I'm just confusing myself about what is even possible (or helpful).
@Ben,
Ah, ok--on all counts. :-) So first, I'm not aware of any such IIS setting but someone else may chime in with one.
Still, this may be a good point at which to talk about how CF (Lucee and ACF since CF2018) do indeed expose environment variables in the server scope, implicitly. In both, they are in the server.system.environment struct. And those could be passed into CF/Lucee on startup via the -D jvm args (which can name any simple var=value pair you want).
Or if you want the flexibility of a .env file, you could load that in with onserverstart, so that it would be available to all apps immediately on their first execution. (And as you may know, if you use containers--whether the images from Adobe, Lucee, or Ortus--those can also implicitly load env vars from a file and otherwise, whether using Docker, Docker Compose, Kubernetes, or the like, and those would be exposed in the server scope as discussed above.)
Finally, as you showed in a 2015 post, one can also access env vars in CFML using Java objects and methods instead. That's from before ACF (and perhaps Lucee) offered the server scope approach. And in the comments, you and others discussed the pros and cons of that, as well as the onserverstart. Maybe it does make sense for you in this case. :-)
I just wanted to say thank you to you two, @Ben.Nadel and @Charlie.Arehart. I've been reading your blogs and notes for ages -- I started with CFv2 in the Allaire days.
I still have, and occasionally wear, my blue cosmos shirt that I received from some CFUG convention in Austin way back when (may have been Macromedia days...)
Post A Comment — ❤️ I'd Love To Hear From You! ❤️
Post a Comment →