Yesterday, I was trying to use the arrayContains() method introduced in ColdFusion 9. Unfortunately, it wasn't working. Even when I double-checked all the values in play, arrayContains() kept telling me that a known value was not in the given array. I tried switching over to arrayFind(), but unfortunately, this exhibited the same broken behavior. After about 20 minutes of debugging, I finally isolated the behavior to an odd interaction between deserialized Form data and database integers.
NOTE: I am running ColdFusion 9.0.1. I have not checked to see if this behavior also exists in ColdFusion 10.
Ok, so imagine this scenario: the web-user submits a list of IDs in a form post. The client serializes the data, posts it to the server, where ColdFusion is then used to deserialize it. This deserialized collection is then tested against a query from the database, and records are updated as necessary.
Seems rather straightforward, right? Well, as it turns out, there's something about the serialization / deserialization process that prevents INT values coming out of the database from being "found" in the deserialized collection being submitted by the user. To see this in action, take a look at the following code:
<cfoutput> <!--- Get our users from the database. In this database schema, the [ID] column is a INT. ---> <cfquery name="users" datasource="testing"> SELECT id, name FROM `user` <!--- Make sure we have IDs for our example. ---> WHERE id IN ( 1, 2, 3, 4, 5 ) ORDER BY id ASC LIMIT 5 </cfquery> <!--- Now, let's pretend that the user submitted some data in the form of an array of user IDs. And, that we have to check those IDs against the collection in the database. That's why we are passing it through serialization / deserialization - to mimic the data transformation for a FORM post. ---> <cfset selectedUserIDs = deserializeJson( serializeJson( [ 1, 4 ] ) ) /> <!--- Output the users. ---> Selected User IDs: #arrayToList( selectedUserIDs )#<br /> <br /> <!--- Loop over the users to see if any of them match the IDs submitted by the user form data. ---> <cfloop query="users"> User #users.id# selected: <!--- Use database ID, as-is. ---> #arrayContains( selectedUserIDs, users.id )# - <!--- Use "stringified" user ID. ---> #arrayContains( selectedUserIDs, toString( users.id ) )# <br /> </cfloop> <br /> <!--- As a sanity check, let's try different values manually. ---> As Number: #arrayContains( selectedUserIDs, 1 )#<br /> As String: #arrayContains( selectedUserIDs, "1" )#<br /> As Java: #arrayContains( selectedUserIDs, javaCast( "int", 1 ) )#<br /> </cfoutput>
Here, I am manually running the data through serializeJson() and deserializeJson() in order to mimic the form post lifecycle. Then, I see which known user IDs correspond to the ones submitted by the user. When I run the above code, I get the following output:
Selected User IDs: 1,4
User 1 selected: NO - YES
User 2 selected: NO - NO
User 3 selected: NO - NO
User 4 selected: NO - YES
User 5 selected: NO - NO
As Number: YES
As String: YES
As Java: YES
The important part is the middle output, where I am looping over the users and checking to see if each user ID is found in the submitted collection. For each user record, I am performing two checks: one with the raw database value; one with a "stringified" value. And, as you can see from the output, only the stringified version can be found in the deserialized collection.
After to I perform the query loop, I then run a few sanity checks to see if I can manually find the ID, "1", in the collection. I try using a number, a string, and a Java int. All three can be found.
Super odd, right? Unless I am totally missing something. Also, if it wasn't clear from my introductory paragraph, I found that both arrayFind() and arrayContains() are adversely affected by this interaction.
Want to use code from this post? Check out the license.