ColdFusion null Gotcha

As you probably know, CFML doesn't have any concept of nulls within it's own type system. It has some facilities for dealing with nulls due to interfaces with other type systems (e.g., from Java or JavaScript).  At least on ColdFusion, it's not perfect.  Consider this code:

s = {
  'javaCast' = javaCast('null', 0),
  'json' = deserializeJson('null')
};
structKeyExists(s, 'javaCast'); // false
structKeyExists(s, 'json');     // true
structKeyExists(s, 'missing');  // false
isNull(s.javaCast); // true
isNull(s.json);     // false
isNull(s.missing);  // true

As you can see, there is no difference between null and missing.  However, the null from the JSON gets deserialized not as a null, but as "null" (i.e., the string containing the letters 'n', 'u', 'l', and 'l'). Wait, what?

I don't think there should be any confusion about how contrived this example is.  However, the issue manifests itself any time you're deserializing JSON packets with ColdFusion.  So watch out.

8 responses to “ColdFusion null Gotcha”

  1. Matt Osbun

    Treating a null value of a struct as if it were missing is dangerous, I think, and should probably be reported as a bug. I have a hard time imagining how that behavior could be right.

    With the serialization, however, I'm not sure that's a bug. Or even all that unusual or unexpected. I did a quick test in C#:

    StringBuilder sb = new StringBuilder();
    new JavaScriptSerializer().Serialize(null, sb);

    At the end of this, sb will have the string value of "null", and sb.ToString() == null will evaluate as false. Which makes sense, really. JSON serializes an object and returns a string value. If a null value didn't have some kind of string representation, I think it would break the JSON formatting.

  2. Ben Nadel

    Another interesting thing about NULL struct keys is that they are still available during struct iteration (ie. collection loop). I ran into that once when looping over non-required CFArgument keys — they were in the loop but caused a problem when I tried to reference them.

  3. D

    So is there a way to test the difference between {"name" : null} and {"name" : "null"} after deserialization?

  4. D

    That didn't work (using CF9; is it different in CF10?). The following returned YES for both tests, and keys in both contained the string value "null":

    <cfscript>
    map1 = DeserializeJSON('{"name" : null}');
    map2 = DeserializeJSON('{"name" : "null"}');
    </cfscript>
    
    <cfoutput>
    key 'name' exists in map1:  #structKeyExists(map1, 'name')#
    key 'name' exists in map2:  #structKeyExists(map2, 'name')#
    </cfoutput>