I can't figure out why my find is not working.
I have a structure created from deserializing some JSON. I get the key list from the structure and then try to find each value. 2 of the keys from the key list don't seem to exist.
The replace function is because I had to replace the %22 character encodings with ~ because it kept getting decoded midflight. That replace is just to put the double quotes back.
<cfscript>
jsonIn = deserializeJSON(replace(result,"~","""","all"));
typeList = "";
for(x=1;x lte listLen(structKeyList(jsonIn[1]));x++){listAppend(typeList,"VarChar");}
result = queryNew(structKeyList(jsonIn[1]),"");
for(row in jsonIn) {
queryAddRow(result);
writeDump(row);
for(cell in structKeyList(row)) {
writeOutput(cell & " found: " & structKeyExists(row,cell) & "<br/>");
//querySetCell(result, cell, row.find(cell));
}
}
writeDump(result);
</cfscript>
This is what I see in output:
If I do a compare in those two cases I get a "-1", but I can't see why the strings are different (or how they can be since both are recovered by pulling from the key list of the object.)
EDIT
The error message is misleading. The problem is in the "undefined" values for those two keys. Changing the json "null" to space solved it.
Welcome to null
in ColdFusion. Let me explain why a key does and does not exist.
First of all, let's see why many developers new to CFML are confused. One of the first things you learn is:
<cfquery>
SELECT NULL AS cellWithNull;
</cfquery>
will turn out as
ColdFusion has no native support for NULL
and will treat this value as an empty string.
At some point, you might need to pass a real null
value to a Java method.
// Java
x = new org.example.FooBar();
x.someMethod(null);
Yet if you try to use or define a variable with null in CFML, such as:
y = null;
ColdFusion will throw an exception: Variable NULL is undefined.
ColdFusion has no native support for null
. In fact, it does not even recognize null
and will attempt to resolve it as a variable by name instead.
// this will work
null = "";
y = null;
// y = [empty string]
// ColdFusion
x = createObject("java", "org.example.FooBar").init();
x.someMethod(javaCast("null", ""));
javaCast("null", "")
exists for this use case. (The second argument has no meaning btw.)
x = deserializeJSON('{ "key": null }');
will turn out as
ColdFusion has no native support for ... wait, what? Exactly, when deserializing null
, the value will be treated as undefined
, making the key to the value exist and not exist at the same time.
structKeyList(x)
>> "key"
structKeyArray(x)
>> [ "key" ]
structKeyExists(x, "key")
>> false
isNull(x.key)
>> true
isNull(x["key"])
>> true
x.key
>> Exception: Element KEY is undefined in X.
writeDump(x["key"])
>> undefined
writeOutput(x["key"])
>> [empty string]
When iterating over keys of a struct, always check if the value behind the key is "defined" according to ColdFusion's standard. Use either structKeyExists
or isNull
to check the value.
x = deserializeJSON('[ null ]');
will turn out as
You know the drill by now.
arrayLen(x)
>> 1
isNull(x[1])
>> Exception: Element 1 is undefined in a Java object of type class coldfusion.runtime.Array.
writeDump(x[1])
>> Exception: Element 1 is undefined in a Java object of type class coldfusion.runtime.Array.
writeOutput(x[1])
>> Exception: Element 1 is undefined in a Java object of type class coldfusion.runtime.Array.
arrayIsDefined(x, 1)
>> false
Always check with arrayIsDefined before accessing an array index, if you expect the array to contain null values.
Not returning any value in a function, yet assigning the return to a variable will also turn out undefined
.
function foo() { return; }
x = foo();
writeDump(x);
>> Exception: Variable X is undefined.
yet
x = { key: foo() };
writeDump(x);
It's worth noting that newer versions of ColdFusion (and Lucee in general) support native null values. It still feels clunky working with them.