The previous lesson consumed JSON from an external API. This one builds it, returning a well-structured JSON response from your own ColdFusion code, with serializeJSON, and a couple of real gotchas that trip up a lot of people the first time: struct key casing, and what a remote CFC method actually returns by default.
Learning Objectives
After completing this lesson, you'll be able to:
- Serialize a struct, array, or query to JSON with serializeJSON.
- Control how a query serializes: row, column, or array-of-structs.
- Return JSON correctly from both a plain .cfm page and a remote CFC method.
- Know the real, engine-specific difference in how struct key casing actually gets preserved (or doesn't).
How a JSON Response Actually Goes Out
Struct / Array / Query
serializeJSON()
JSON String
Sent With the Right Content-Type
A Basic Manual JSON Response
<cfset responseData = { "status" = "success", "code" = 200 }>
<cfset jsonString = serializeJSON(responseData)>
<cfcontent type="application/json" reset="true">
<cfoutput>#jsonString#</cfoutput>responseData = { "status": "success", "code": 200 };
jsonResponse = serializeJSON(responseData);
cfcontent(type = "application/json", reset = "true");
writeOutput(jsonResponse);reset="true" clears any output already buffered on the page before this point, without it, stray whitespace or earlier output can corrupt the JSON the client actually receives.
serializeJSON's Arguments
| Argument | Meaning |
|---|---|
| data | The struct, array, or query to serialize (required) |
| queryFormat | row (default), column, or struct, controls how a query serializes |
| useSecureJSONPrefix | Prepends a security prefix to the output (default false) |
| useCustomSerializer | Whether to use a custom serializer if one is defined (default true) |
A Real Example: The Three Query Serialization Formats
The default row format is genuinely awkward for a modern frontend, struct is what most JavaScript code actually wants.
| queryFormat | Output Shape |
|---|---|
| row (default) | {"COLUMNS":["CITY","STATE"],"DATA":[["Newton","MA"],["San Jose","CA"]]} |
| column | {"ROWCOUNT":2,"COLUMNS":["CITY","STATE"],"DATA":{"CITY":["Newton","San Jose"],"STATE":["MA","CA"]}} |
| struct | [{"CITY":"Newton","STATE":"MA"},{"CITY":"San Jose","STATE":"CA"}] |
jsonOutput = serializeJSON(myQuery, "struct");
A Real Correction: Struct Key Casing Isn't Fixed by Just Quoting Keys
A common claim is that quoting a struct literal's keys, {"name": "John"} instead of {name: "John"}, preserves lowercase casing in the resulting JSON. That's genuinely true on Lucee, but not on Adobe ColdFusion.
| Engine | Actual Behavior |
|---|---|
| Adobe ColdFusion | Struct keys are internally represented as all-uppercase, and serialize as all-uppercase, regardless of how the literal quoted them. Fixing this requires this.serialization.preservecaseforstructkey = true in Application.cfc, or the equivalent Administrator setting under Server Settings > Settings. |
| Lucee | Quoted keys in a struct literal preserve their case; unquoted (bare word) keys get uppercased. |
component {
this.serialization.preservecaseforstructkey = true;
}A Real Gotcha: Remote CFC Methods Don't Return JSON by Default
By default, a remote CFC function's return value serializes to WDDX, not JSON. Getting JSON out of it requires asking for it explicitly.
remote struct function getProductData() returnformat="json" {
return { status: "success", productId: 101 };
}returnformat="json" can be set on cffunction itself, passed as a ?returnformat=json parameter in the calling URL, or set via a CFC proxy's setReturnFormat function, the URL parameter is exactly what ColdFusion's own Ajax bind expressions and cfajaxproxy generate automatically.
Common Beginner Mistakes
Assuming quoted struct literal keys fix case on Adobe ColdFusion
That's Lucee's behavior specifically. On Adobe ColdFusion, keys are uppercased regardless of quoting, this.serialization.preservecaseforstructkey=true is the actual fix.
Assuming a remote CFC method returns JSON automatically
The default is WDDX, not JSON. returnformat="json" (on the function, in the URL, or via a proxy) is required to actually get JSON back.
Forgetting reset="true" on cfcontent for a manual JSON response
Any output already buffered before that point stays in the response, potentially corrupting the JSON with stray content ahead of it.
Serializing a query with the default row format for a modern JavaScript frontend
row's columns/data-arrays shape is awkward to consume directly. struct produces the array-of-objects shape most frontend code actually expects.
Best Practices
- Use queryFormat="struct" when a query's JSON is going to a modern frontend.
- Set this.serialization.preservecaseforstructkey=true on Adobe ColdFusion (or rely on Lucee's quoted-key behavior) when lowercase/camelCase JSON keys actually matter.
- Set returnformat="json" explicitly on a remote method, rather than depending on the WDDX default.
- Always reset="true" before manually writing a JSON response with cfcontent.
Interview Questions
Does quoting a struct literal's keys preserve their case in JSON on Adobe ColdFusion?
No, that's Lucee-specific behavior. Adobe ColdFusion uppercases struct keys during serialization regardless of quoting, this.serialization.preservecaseforstructkey=true is the real fix there.
What format does a remote CFC method return by default, if returnformat isn't specified?
WDDX, not JSON. returnformat="json" needs to be set explicitly, whether on the function, as a URL parameter, or via a proxy's setReturnFormat.
What's the difference between serializeJSON's row, column, and struct query formats?
row (the default) produces separate columns and data arrays. column groups values by column name. struct produces an array of individual row objects, the shape most modern frontend code actually wants.
Summary
In this lesson, you returned JSON manually from a .cfm page, controlled query serialization format with serializeJSON, covered the real cross-engine difference in struct key casing (quoted keys on Lucee vs. an explicit setting on Adobe ColdFusion), and the real WDDX-by-default gotcha on remote CFC methods.
What's Next?
The next lesson covers authentication basics: verifying who's calling your API before letting them use it.