June 9, 2021
Properly handled quoted yes/no with serializeJSON in CF2021 and CF2018, solvable in CF2016
Comments
(1)
June 9, 2021
Properly handled quoted yes/no with serializeJSON in CF2021 and CF2018, solvable in CF2016
ColdFusion troubleshooter
Wizard 146 posts
Followers: 115 people
(1)

Did you know that CF2021 and CF2018 now “properly” handle serializing to json strings with the quoted values of “yes” and “no”, leaving them as strings rather than converting them to booleans true and false?

And did you know that if you are on CF2016 (which does convert them), you can solve that with just a couple of lines of code? And that same solution can be used if you are on CF2018 or above and do WANT a string like “yes” to be converted to a boolean (in a serializejson). Perhaps more important, that same code (and even an application-level variant) can help with other matters with JSON serialization datatypes.

In this post, I’ll offer some code to demonstrate the specific problem and the difference in recent CF versions, then I’ll go on to share more about other matters of JSON serialization and ways CF attends to those, as well as where to find more info on all this.

It all started with a question

This post was prompted by a question I got, pointing to an Adobe tracker bug ticket where the problem was first reported…in 2012. In that case, the OP was annoyed by this conversion of quoted values like “No” to false, because in their case they may be a last name of a person (“No”) or the country code for Norway (“NO”).

But when I tested things, I found that they did work in CF2021 and 2018 as expected, and the result could be easily created via a simple code change in CF2016 (this latter point had been indicated in the ticket’s comments). I started to write this as a comment there (to clarify about CF2021 and 2018), both for the sake of that person asking and for others on the ticker or who may find it in the future. But my comment submission kept failing to post (which was odd, but perhaps because I was offering code, or json, or hyperlinks).

So I punted and decided to create this blog post instead, and as happens I started to elaborate a bit more, since a blog post tends to stand differently over time.

And I will point to this post in the ticket…but the news may well help folks who might not ever come across that ticket.

The good news in CF2021 and CF2018

First, here is the code that was offered in the ticket, in a comment from 2013 (so pardon the tags):

<!--- Bug: SerializeJSON turn "NO" into false --->
<cfset testStruct = {"test" = "NO"}> 
<cfdump var="#testStruct#"> <!--- the test key is "NO" --->
<cfset json = SerializeJSON(testStruct)>
<cfdump var="#json#"> <!--- Test is now false --->

The concern raised then was that CF was producing a result (in that last cfdump) of:

{"test":false}

But the good news is that CF2021 and 2018 (at least in their latest versions, updates 1 and 11, respectively) do now produce:

{"test":"NO"}

Again, though, sadly CF2016 (even it’s final update 17 in Mar 2021) does still produce:

{"test":false}

That issue with CF2016 can actually be solved with a couple of alternatives, which do actually work as well beyond CF2016, of course. I’ll discuss those in a moment.

To be clear, if the value had been left as false (no quotes), the result would instead be {“test”:false}, in all three CF versions.

And FWIW, in CF2018 and 2021, if the value of that test variable was “yes”, “YES”, “true”, or “TRUE”, then those would indeed be the value produced, while if it was left as just true (no quotes), the result would instead be {“test”:true}.

Also, you may note that the code above used quotes around the name of the variable on the left side of the equal sign (=). That causes the variable output by serializeJSON to be the case indicated. If it were not quoted, CF would create a capitalized name of TEST in the json output.

One workaround for those on CF2016: setmetadata

As noted above about CF2016, and as noted by comments from others in that tracker ticket (from the year 2016), this issue about quoted booleans was indeed addressed by CF2016 update 2, which solved this in various ways, the first being a new setmetadata member function.

With it, you’d set the desired datatype for a given struct key (it can also be used with arrays. More in a moment.) So here is how to solve it for that example code above (I’ve kept it as tags for the sake of consistency):

<!---changing the default serialization by specifying the datatype of the "test" struct key to be string --->
<cfset metadata = {test: {type:"string"}}>
<cfset teststruct.setMetadata(metadata)>
<cfdump var="#SerializeJSON(teststruct)#">

which will again produce the desired:
{“test”:”NO”}

And of course, that works in CF2018 and 2021 as well (for those who may put such setmetadata code in place in 2016–though again they may not need it anymore in at least a simple boolean case like this).

Try it yourself, on cffiddle.org

For those who may not have these later CF versions installed and want to test their own code variants, try  them on Adobe’s cffiddle.org site, which lets you run CF2021, 2018, and 2016 (in their latest updated versions).

And to see this specific code above running (both variants), here’s a link to it.

Another alternative: structSetMetadata

You may have caught that I indicated (and showed) that setmetadata being used as a member function of a struct (or function). FWIW, the same CF2016 update 2 also introduced a separate but related structSetMetadata, which accepts args of a struct and the metadata to use for its keys (for those who may have reason to prefer a true CFML function to a member function).  I’ll leave you to look into that if interested.

(To be clear, I have had no hand in any of this nor really any “dog in the fight”. So I don’t know how or why both the member function and true function options were offered.  I’m just clarifying things because, again, someone asked me about it, and I started looking into it, and started writing. 🙂

There’s also a corresponding structGetMetadata, which takes only a struct name and returns a result that shows the datatypes for the keys in that struct.

Handling arrays as well: setmetadata and arraySetMetadata

While the focus in the tracker ticket (and so far above) has been on structs, and controlling serialization of their keys, note that the same can be done with arrays as well (including structs within arrays).

First, the same setmetadata member function can be used with an array just as easily as with a struct. Second, there is also an arraySetMetadata (and arrayGetMetaData) function, which also were added also in CF2016 update 2.

In case it helps, here’s an example of both approaches (as script, for those who prefer that). The first comes from the docs page for the arraySetMetadata function :

<cfscript>
inputs = ["2500.12", 4.0, "Yes", "False", "339090", {"q1": "Yes"}, ["1","yes","3","no","null","null"]];
metadata={items: ["numeric", "integer", "string", "boolean", "string", {q1: {type:"string",ignore:true}},
{items: ["integer","boolean","string","string","string","null"]}]};
inputs.setmetadata(metadata);
writeoutput(serializeJSON(inputs));
</cfscript>

And though that doc page doesn’t show it, here’s a variant using the member function approach:

<cfscript>
inputs = ["2500.12", 4.0, "Yes", "False", "339090", {"q1": "Yes"}, ["1","yes","3","no","null","null"]];
metadata={items: ["numeric", "integer", "string", "boolean", "string", {q1: {type:"string",ignore:true}},
{items: ["integer","boolean","string","string","string","null"]}]};
ArraySetMetaData(inputs,metadata);
writeoutput(serializeJSON(inputs));
</cfscript>

The two approaches will produce the identical result. In both of CF2021 and CF2018, each variant returns:

[2500.12,4.0,"Yes",false,"339090",{},[1,true,"3","no","null","null"]]

Curiously, in CF2016, each variant returns a slightly different result:

[2500.12,4,"Yes",false,"339090",{},[1,true,"3","no","null","null"]]

Notice that the second value (“4.0”) becomes a “4”, which  actually seems “more correct” to me, since the datatype for that is declared above to be an integer. But I don’t live in the world of JSON and again don’t have a dog in this fight. If this bothers anyone, I’ll leave it for them take up with Adobe if they think it’s seriously amiss.

Controlling datatype serialization for more than just booleans

As long as that last example showed other datatypes, I’ll point out that all these functions and member functions do support still more datatypes. From the docs for serializeJSON, it indicates that the supported types are:

string, numeric, integer, boolean, date, array, and struct

Yet another alternative: setting datatypes at the application level

As this post has gone rather far afield of its original goal, I may as well close with mentioning also that this CF2016 update 2 support provided as well for enabling serialization not just at the code level but even at the application level, with an available this.serialization.structmetadata. (I am not aware that there is a corollary this.serialization.arraymetadata.)

I’ll leave you to check the docs for leveraging that feature, and to decide whether and where it may make sense for you. You can find mention of this in the docs about app-level settings in application.cfc, but there’s even more in the other docs where all this (above) is discussed…

Finding more info

Besides the links to specific functions and features above, note that all this matter of datatypes for serialization is discussed in a couple other key places.

First, see the substantial docs page for the serializeJSON function has quite a lot of discussion of serialization issues and solutions, including more than just what I’ve shared above to include dealing with serialization of a query/queries, custom serialization, and more. It also offers links to running sample code in the cffiddle.org site.

Next, see also the release notes for CF2016 update 2, when all this was introduced. Sometimes such alternative docs can offer more than may have ended up in the final docs.

Wrapping up

I realize that when it comes to datatypes and json serialization with CF, it’s been a bumpy road. But again, this was sparked by a discussion about the original issue raised in that first tracker ticket I’d mentioned. And least it does seem “case closed” on that specific matter, which is good to see. 🙂

I hope the info here may help folks hitting that or even other matters of datatypes and json serialization. There is certainly a lot more about CF serialization and deserialization of data, but this post is not trying to cover ALL those matters, so check out that doc for serializeJSON and related functions for still more.


For more blog content from Charlie Arehart, see his posts here as well as his posts at carehart.org. And follow him on Twitter and other social media as carehart.

1 Comment
2023-09-08 13:09:09
2023-09-08 13:09:09

For those on cf11 or earlier, there are various workarounds you might want to consider, listed as answers or comments on this SO post.

Like
Add Comment