I'm using Ben Nadel's Reading Excel Files With ColdFusion And POI codes at https://www.bennadel.com/blog/472-reading-excel-files-with-coldfusion-and-poi.htm to read my excel file.
With his example codes I can read the excel file when rows in my excel are made uniform. Ben mentioned about this in his comment. But my excel however does not always has uniform rows. Some has birth date, some does not have gender, etc.
Ben Nadel's codes produce error once it hits the empty cell. It says, the
objCell variable does not exist. The following code crashes:
<cfset objCell = objRow.GetCell(JavaCast( "int", intCell )) />
Does anyone has an example and do not mind sharing the codes that can also read empty cells when looping?
I'm pasting Ben's codes here:
<!---
Create the Excel file system object. This object is
responsible for reading in the given Excel file.
--->
<cfset objExcelFileSystem = CreateObject(
"java",
"org.apache.poi.poifs.filesystem.POIFSFileSystem"
).Init(
CreateObject(
"java",
"java.io.FileInputStream"
).Init(
ExpandPath( "./jenna_jameson.xls" )
)
) />
<!---
Get the workbook from the Excel file system object that
we just created. Remember, the workbook contains the
Excel sheets that have our data.
--->
<cfset objWorkBook = CreateObject(
"java",
"org.apache.poi.hssf.usermodel.HSSFWorkbook"
).Init(
objExcelFileSystem
) />
<!---
For this demo, we are only interested in reading in the
data from the first sheet. Remember, since Java is zero-
based, not one-based like ColdFusion, the first Excel
sheet is at index ZERO (not ONE).
--->
<cfset objSheet = objWorkBook.GetSheetAt(
JavaCast( "int", 0 )
) />
<!---
We are going to build a ColdFusion query that houses the
Excel data, but we don't know anything about the data
just yet. So, just create the place holder for the query
and then we will add to it when we have more information.
--->
<cfset qCell = "" />
<!---
Get the Excel sheet's row iterator. This appears to be some
sort of implementation of the Java class java.util.TreeMap,
but I don't know much about that. What I do know, is that
this will allow us to loop over the rows in the Excel file
until there are no more to loop over. The interface for it
looks like the standard iterator interface.
--->
<cfset objRowIterator = objSheet.rowIterator() />
<!---
User the row iterator to loop over all the physical rows in
the Excel sheet. This condition checks to see if we have a
row to read in. At this point, the iterator is NOT pointing
at a valid Excel data row.
--->
<cfloop condition="objRowIterator.HasNext()">
<!---
We have determined that we have a valid row to read.
Now, move the iterator to point to this valid row.
--->
<cfset objRow = objRowIterator.Next() />
<!---
Get the number of physical cells in this row. While I
think that this can possibly change from row to row,
for the purposes of this demo, I am going to assume
that all rows are uniform and that this row is a model
of how the rest of the data will be displayed.
--->
<cfset intCellCount = objRow.GetPhysicalNumberOfCells() />
<!---
Check to see if the query variable we have it actually
a query. If we have not done anything to it yet, then
it should still just be a string value (Yahoo for
dynamic typing!!!). If that is the case, then let's use
this first data row to set up the query object.
--->
<cfif NOT IsQuery( qCell )>
<!---
Create an empty query. Doing it this way creates a
query with neither column nor row values.
--->
<cfset qCell = QueryNew( "" ) />
<!---
Now that we have an empty query, we are going to
loop over the cells COUNT for this data row and for
each cell, we are going to create a query column
of type VARCHAR. I understand that cells are going
to have different data types, but I am chosing to
store everything as a string to make it easier.
--->
<cfloop index="intCell" from="0" to="#(intCellCount - 1)#"
step="1">
<!---
Add the column. Notice that the name of the
column is the text "column" plus the column
index. I am starting my column indexes at ONE
rather than ZERO to get it back into a more
ColdFusion standard notation.
--->
<cfset QueryAddColumn(qCell,"column#(intCell + 1)#",
"CF_SQL_VARCHAR",ArrayNew( 1 )) />
</cfloop>
</cfif>
<!---
ASSERT: Whether we are on our first Excel data row or
our Nth data row, at this point, we have a ColdFusion
query object that has the proper columns defined.
--->
<!---
Add a row to the query so that we can store this row's
data values.
--->
<cfset QueryAddRow( qCell ) />
<!--- Loop over the cells in this row to find values. --->
<cfloop index="intCell" from="0" to="#(intCellCount - 1)#"
step="1">
<!---
When getting the value of a cell, it is important
to know what type of cell value we are dealing
with. If you try to grab the wrong value type,
an error might be thrown. For that reason, we must
check to see what type of cell we are working with.
These are the cell types and they are constants
of the cell object itself:
0 - CELL_TYPE_NUMERIC
1 - CELL_TYPE_STRING
2 - CELL_TYPE_FORMULA
3 - CELL_TYPE_BLANK
4 - CELL_TYPE_BOOLEAN
5 - CELL_TYPE_ERROR
--->
<!--- Get the cell from the row object. --->
----- **When it hit an empty cell CF throws error** ---
<cfset objCell = objRow.GetCell(JavaCast( "int", intCell)) />
<!--- Get the type of data in this cell. --->
<cfset objCellType = objCell.GetCellType() />
<!---
Get teh value of the cell based on the data type.
The thing to worry about here is cell forumlas and
cell dates. Formulas can be strange and dates are
stored as numeric types. For this demo, I am not
going to worry about that at all. I will just grab
dates as floats and formulas I will try to grab as
numeric values.
--->
<cfif (objCellType EQ objCell.CELL_TYPE_NUMERIC)>
<!---
Get numeric cell data. This could be a
standard number, could also be a date value.
I am going to leave it up to the calling
program to decide.
--->
<cfset objCellValue = objCell.GetNumericCellValue() />
<cfelseif (objCellType EQ objCell.CELL_TYPE_STRING)>
<cfset objCellValue = objCell.GetStringCellValue() />
<cfelseif (objCellType EQ objCell.CELL_TYPE_FORMULA)>
<!---
Since most forumlas deal with numbers, I am
going to try to grab the value as a number. If
that throws an error, I will just grab it as a
string value.
--->
<cftry>
<cfset objCellValue = objCell.GetNumericCellValue() />
<cfcatch>
<!---
The numeric grab failed. Try to get the
value as a string. If this fails, just
force the empty string.
--->
<cftry>
<cfset objCellValue = objCell.GetStringCellValue() />
<cfcatch>
<!--- Force empty string. --->
<cfset objCellValue = "" />
</cfcatch>
</cftry>
</cfcatch>
</cftry>
<cfelseif (objCellType EQ objCell.CELL_TYPE_BLANK)>
<cfset objCellValue = "" />
<cfelseif (objCellType EQ objCell.CELL_TYPE_BOOLEAN)>
<cfset objCellValue = objCell.GetBooleanCellValue() />
<cfelse>
<!--- If all else fails, get empty string. --->
<cfset objCellValue = "" />
</cfif>
<!---
ASSERT: At this point, we either got the cell value
out of the Excel data cell or we have thrown an
error or didn't get a matching type and just
have the empty string by default. No matter what,
the object objCellValue is defined and has some
sort of SIMPLE ColdFusion value in it.
--->
<!---
Now that we have a value, store it as a string in
the ColdFusion query object. Remember again that my
query names are ONE based for ColdFusion standards.
That is why I am adding 1 to the cell index.
--->
<cfset qCell[ "column#(intCell + 1)#" ][ qCell.RecordCount ] =
JavaCast( "string", objCellValue ) />
</cfloop>
</cfloop>
<!---
At this point, the excel data should be in a ColdFusion
query object. However, if the query did not contain any
record, then the row iterator was never launched which
mean we never actually defined a query. As one final check
just make sure we are dealing with a query.
--->
<cfif NOT IsQuery( qCell )>
<!--- Just define an empty query. --->
<cfset qCell = QueryNew( "" ) />
</cfif>
Perform below two steps to read empty cells also in ColdFusion-8:
Add a variable outside loops to represent HSSFCell. Example:
<cfset jCell = createObject("java", "org.apache.poi.hssf.usermodel.HSSFCell")>
Use function objRow.GetLastCellNum() instead of objRow.GetPhysicalNumberOfCells(). Example:
<cfset intCellCount = objRow.GetLastCellNum() />
Add a <cfif> condition to set value of variable objCellType based on existence of variable objCell. Example:
<cfset objCell = objRow.GetCell(JavaCast( "int", intCell)) />
<cfif structKeyExists(variables, "objCell")>
<cfset objCellType = objCell.GetCellType() />
<cfelse>
<cfset objCellType = jCell.CELL_TYPE_BLANK />
</cfif>
Replace variable name objCell in all references to objCell.CELL_TYPE_[type_name] with variable name created in step 1. Example:
<cfif (objCellType EQ jCell.CELL_TYPE_NUMERIC)>
I have a excel sheet
<cfspreadsheet action="read" src="test.xlsx" sheet=1 rows="2-7" format="html" name="csvData">
How would i get the value of specic column and output the value?
For example:
Actual
testtet test 9040416.9299998
test test tettest 6406129.51
I would like to get column 2, row 3 which is the value of 6406129.51.
This is how you can get the value of specific rows and column form excel.
<cfspreadsheet action="read" src="test.xlsx" rows="2-7" sheet="1" query="test"/>
<cfdump var="#test["col_2"][3]#">
I'm getting and error when I tried to do a query of query.
Table named allData was not found in memory. The name is misspelled or the table is not defined.
I have an excel document and I'm outputting to a coldfusion var called allData, then I'm doing a query on that var. but I'm getting an error:
What am I doing wrong? The first dump shows the table appropriately.
function name="validateExcel" access="public" output="yes" returnType="void"
hint="search for dogs">
<cfspreadsheet
action="read"
src="#SESSION.theExcelFile#"
headerrow= "1"
excludeHeaderRow = "true"
query = "allData"
rows = "1-25"/>
<cfdump var = "#allData#"/>
<cfset rotCheck = new Query(
sql = "SELECT * FROM allData where dogType like '%rot'",
dbtype = "query"
) />
<cfset dogResult = rotCheck.execute().getResult() />
<cfdump
var = "#dogResult#" />
</cffunction>
(From comments ...)
I have to run, but short answer - the query variable from the spreadsheet is not in scope within the Query.cfc. (The documentation on Query.cfc is somewhat lacking IMO. ) Either pass in the query object as a parameter ie new Query(...., allData=allData) or use a <cfquery> instead.
Given that the dump works, the allData variable exists. A cfquery tag with the appropriate attributes will solve your problem for you.
I am new to ColdFusion. Anyone know why this code is not working. When I leave the form null it is not showing 100 in the database.
<cfif isdefined("FORM.Percentage")>
<cfset Form.Percentage = #Form.Percentage#>
<cfelse>
<cfset Form.Percentage = 100>
</cfif>
<cfquery name="percent" datasource ="abc">
Insert into Employees
(Percentage)
Values
(#Form.Percentage#)
</cfquery>
If you have a textbox it is submitted to the form even if it's left blank, so you want to check if the field was left blank. If it was then you can set the default.
You'll also want to do some server side validation that the value is a number and use cfqueryparam for inserting your value into the database.
<cfif NOT len(trim(FORM.Percentage))>
<cfset Form.Percentage = 100>
<cfif>
<cfquery result="percent" datasource="abc">
Insert into Employees (Percentage)
Values (
<cfqueryparam cf_sql_type="cf_sql_integer" value="#Form.Percentage#">
)
</cfquery>
When using cfquery with an INSERT the name attribute doesn't provide anything. Using result would allow you to view some data about the query if needed, but generally it shouldn't be used.
You could also have dumped form to the screen by using <cfdump var="#form#"> to see what it was returning. If you want to check that the key exists for a radio button or checkbox you can use structKeyExists(form,'myCheckbox') rather than using isDefined().
Why does the following work in CF10 but not CF9?
<cfset out="">
<cfif isQuery( arguments.values ) >
<cfloop query="#arguments.values#" >
<cfset out = '#out#<option value="#value#">#label#</option>'>
</cfloop>
</cfif>
CF9 states that "Complex object types cannot be converted to simple values." for the line containing the cfloop. I'm using the Coldbox framework and it's debugger information shows that arguments.values is a query with Label & Value columns.
Prior to CF10, the query attribute of cfloop can only be a string - the name of the query - not the variable itself.
So, when you put #arguments.values# it is trying to convert the complex query object to a string, to obtain a name, which is where the error comes from.
It works in CF10 because the attribute has been updated to also allow a query value.
side notes:
This line of code can be simplified:
<cfset out = '#out#<option value="#value#">#label#</option>'>
to:
<cfset out &= '<option value="#value#">#label#</option>'>
Also you very likely should be using HtmlEditFormat* on at least label, and perhaps value too.
*(or encodeForHtml if it only needs to work in CF10+)