coldfusionrailolucee

Custom function works in ColdFusion 10/11 but throws error in Lucee


I have the following function, inherited from some CF code. It works, but only in ColdFusion version 10 and 11. I am not concerned with 2016 as no plans for upgrade. I'm trying to run it under Lucee, but it is not working.

<cffunction name="QueryToArray" access="public" returntype="array" output="false" hint="This turns a query into an array of structures.">
  <!--- Define arguments. --->
  <cfargument name="Data" type="query" required="yes" />
  <cfscript>    
    var LOCAL = StructNew(); // Define the local scope.
    LOCAL.Columns = data.getMetaData().getColumnLabels();  // Get the column names as an array.
    LOCAL.QueryArray = ArrayNew(1); // Create an array that will hold the query equivalent.
    for (LOCAL.RowIndex = 1 ; LOCAL.RowIndex LTE ARGUMENTS.Data.RecordCount; 
    LOCAL.RowIndex = (LOCAL.RowIndex + 1)){
        LOCAL.Row = StructNew();
        for (LOCAL.ColumnIndex = 1 ; LOCAL.ColumnIndex LTE ArrayLen(LOCAL.Columns); 
        LOCAL.ColumnIndex = (LOCAL.ColumnIndex + 1)){
            LOCAL.ColumnName = LOCAL.Columns[LOCAL.ColumnIndex];
            LOCAL.Row[LOCAL.ColumnName] = ARGUMENTS.Data[LOCAL.ColumnName][LOCAL.RowIndex];
        }
        ArrayAppend(LOCAL.QueryArray, LOCAL.Row);
    }
    return(LOCAL.QueryArray);
    </cfscript>
</cffunction>

In Lucee, I tried making these changes:

<cffunction name="QueryToArray" access="public" returntype="array" output="false" hint="This turns a query into an array of structures.">
  <!--- Define arguments. --->
  <cfargument name="Data" type="query" required="yes" />
  <cfscript>    
    var LOCAL = StructNew(); // Define the local scope.
    LOCAL.Columns = data.getColumnlist(false); // Get the column names as an array.
    LOCAL.QueryArray = ArrayNew(1); // Create an array that will hold the query equivalent.
    for (LOCAL.RowIndex = 1 ; LOCAL.RowIndex LTE ARGUMENTS.Data.RecordCount; 
    LOCAL.RowIndex = (LOCAL.RowIndex + 1)){
        LOCAL.Row = StructNew();
        for (LOCAL.ColumnIndex = 1 ; LOCAL.ColumnIndex LTE ArrayLen(LOCAL.Columns); 
        LOCAL.ColumnIndex = (LOCAL.ColumnIndex + 1)){
            LOCAL.ColumnName = LOCAL.Columns[LOCAL.ColumnIndex];
            LOCAL.Row[LOCAL.ColumnName] = ARGUMENTS.Data[LOCAL.ColumnName][LOCAL.RowIndex];
        }
        ArrayAppend(LOCAL.QueryArray, LOCAL.Row);
    }
    return(LOCAL.QueryArray);
    </cfscript>
</cffunction> 

but I'm getting this error:

Message: Can't cast String [name,value] to a value of type [Array]
Detail: Java type of the object is java.lang.String
Stacktrace: The Error Occurred in

C:\home\website\wwwroot\controller\production\cfc\kernel.cfc: line 12 
     10: LOCAL.RowIndex = (LOCAL.RowIndex + 1)){
     11: LOCAL.Row = StructNew();
     12: for (LOCAL.ColumnIndex = 1 ; LOCAL.ColumnIndex LTE ArrayLen(LOCAL.Columns); 
     13: LOCAL.ColumnIndex = (LOCAL.ColumnIndex + 1)){
     14: LOCAL.ColumnName = LOCAL.Columns[LOCAL.ColumnIndex];

Solution

  • To get your code to work, all you need to do is change line 6 from

    LOCAL.Columns = data.getColumnlist(false); // Get the column names as an array.
    

    to this

    LOCAL.Columns = data.getColumnNames(); // Get the column names as an array.
    

    However I found a gist here that works in both ColdFusion and Lucee. I decided to create some sample code and test it at https://trycf.com/. I verified that it works in Lucee 4.5 and 5. It also works in ColdFusion 10, 11, 2016 as well. Hope this helps!

    <!--- Create a new three-column query, specifying the column data types ---> 
    <cfset myQuery = QueryNew("Name, Time, Advanced", "VarChar, Time, Bit")> 
    
    <!--- Make two rows in the query ---> 
    <cfset QueryAddRow(MyQuery, 2)> 
    
    <!--- Set the values of the cells in the query ---> 
    <cfset QuerySetCell(myQuery, "Name", "The Wonderful World of CMFL", 1)> 
    <cfset QuerySetCell(myQuery, "Time", "9:15 AM", 1)> 
    <cfset QuerySetCell(myQuery, "Advanced", False, 1)> 
    <cfset QuerySetCell(myQuery, "Name", "CFCs for Enterprise 
            Applications", 2)> 
    <cfset QuerySetCell(myQuery, "Time", "12:15 PM", 2)> 
    <cfset QuerySetCell(myQuery, "Advanced", True, 2)> 
    
    <h4>The query</h4> 
    <cfdump var="#myQuery#">
    
    <h4>The array of objects</h4> 
    <cfset myArray = QueryToArray(myQuery)>
    <cfdump var="#myArray#">
    
    <cfscript>
    /**
    * @hint Returns reasonable array of objects from a cfquery
    */
    public function queryToArray(
        required query  query,
                 string excludeColumns = ""
    ){
    
        if (server.coldfusion.productName == "ColdFusion Server") {
            local.columns = arguments.query.getMetaData().getColumnLabels();
        } else if (server.coldfusion.productName == "Lucee") {
            local.columns = arguments.query.getColumnNames();
        } else {
            local.columns = [];
        }
    
        local.response = arrayNew(1);
    
        for (local.rowIndex=1; local.rowIndex<=arguments.query.recordCount; local.rowIndex++) {
            local.response[local.rowIndex] = structNew();
    
            for (local.columnIndex=1; local.columnIndex<=arrayLen(local.columns); local.columnIndex++) {
                local.columnName = local.columns[local.columnIndex];
    
                if(local.columnName != "" && (arguments.excludeColumns == "" || !listFindNoCase(arguments.excludeColumns, local.columnName))) {
                    local.response[local.rowIndex][local.columnName] = arguments.query[local.columnName][local.rowIndex];
                }
            }
        }
    
        return local.response;
    }
    </cfscript>