How to build structure from cfquery? - json

I have cffunction that should return JSON structure. There is more than 50 columns that I have to return. Instead of building my structure manually I would like to build that dynamically. So first loop through query then loop through each table column. Here is example:
<cffunction name="getRecords" access="remote" output="true" returnformat="JSON">
<cfargument name="userID" type="string" required="true">
<cfset fnResults = StructNew()>
<cfquery name="myQuery" datasource="test">
SELECT
ur_first,
ur_last,
ur_dob,
ur_gender,
ur_email,
ur_address,
... and the rest of the columns
FROM Users
WHERE ur_id = <cfqueryparam value="#trim(arguments.userID)#" cfsqltype="cf_sql_char" maxlength="15">
ORDER BY ur_createDt
</cfquery>
<cfset fnResults.recordcount = myQuery.recordcount>
<cfloop query="myQuery">
<cfset qryRecs = StructNew()>
<cfloop array="#myQuery.getColumnList()#" index="columnName">
<cfset qryRecs.'#columnName#' = URLEncodedFormat('#columnName#')>
</cfloop>
</cfloop>
<cfset fnResults.data = qryRecs>
<cfreturn fnResults>
</cffunction>
This error I'm getting back after Ajax call:
CFML variable name cannot end with a "." character.
The variable qryRecs. ends with a "." character. You must either provide an additional structure key or delete the "." character.
Referencing to this line:
443 : <cfset qryRecs.'#columnName#' = URLEncodedFormat('#columnName#')>
I want to set column name to structure qryRecs like this:
<cfset qryRecs.ur_first = URLEncodedFormat(myQuery.ur_first)>
This way I don't have to set 50 plus columns manually. They all should be created dynamically. If anyone can help please let me know.

I created an ArrayCollection object that can convert a ColdFusion query to a few different JSON formats. Take a look and see if this fits your needs.
For example, this query:
<cfquery name="rs.q" datasource="cfbookclub">
SELECT DISTINCT
bookid,
title,
genre
FROM
books
WHERE
title LIKE <cfqueryparam value="%#arguments.term#%" cfsqltype="cf_sql_varchar" />
ORDER BY
genre, title
</cfquery>
will be converted to this JSON:
{
"data": [
{
"bookid": 8,
"genre": "Fiction",
"title": "Apparition Man"
},
{
"bookid": 2,
"genre": "Non-fiction",
"title": "Shopping Mart Mania"
}
]
}
I'm also working on an update that adds meta data to the return message:
{
"success": true,
"message": "Array Collection created.",
"meta": {
"offset": 0,
"pageSize": 0,
"totalRecords": 0
},
"data": []
};

Related

Coldfusion JSON

Currently I'm getting this output (QueryBean):
But I want the "normal" JSON output, like this one:
[
{
"EventType": "active",
"InstanceId": "6728E65C-XXXX-XXXXX",
"CustomerId": "1000324234",
"Name": "bilderbuchverein Hermsen"
"Amount": 999999,
"StartDate": "August, 01 2019 00:00:00",
"ExpirationDate": null
},
{
"EventType": "active",
"InstanceId": "956FA492-XXXX-XXXXX",
"Name": "Phantasialand"
"CustomerId": "12345678999",
"Amount": 123456789,
"StartDate": "August, 14 2019 00:00:00",
"ExpirationDate": null
}
]
How can I manage to change the output format? My function has the parameter produces="application/json"
<cffunction name="listEvents" access="remote" returnType="any" produces="application/JSON" httpmethod="GET" restpath="/events">
<cfquery datasource="hostmanager" name="plannedInstances">
SELECT * FROM licenses
</cfquery>
<cfreturn plannedInstances>
</cffunction>
To get JSON output data as an array of structure, you can use Application-level defining. In Application.cfc by adding this.serialization.serializeQueryAs = "struct" you can get serialize JSON output data as an array of structure like below.
[
{
"CATEGORYID":20,
"CATEGORYNAME":"php",
"CATEGORYDESC":"php"
},
{
"CATEGORYID":21,
"CATEGORYNAME":"cf",
"CATEGORYDESC":"cf"
},
{
"CATEGORYID":22,
"CATEGORYNAME":".Net",
"CATEGORYDESC":".net"
}
]
I have used the same code with my test table. (please see the example code result screenshot)
Also, you can refer SerializeJSON 'Additional format for query serialization' I think you can't able to handle this issue with produces="application/json"
I hope It's useful for you!
There are two steps to achieve your goal.
Create and return an array of structures instead of a query ( Which fully based on CFML side )
Use the ParseJson method in jQuery to parse the response from CF.
Here is an example of returning an array of structures in JSON format. It creates a single array, and populates it with individual structures inside the query loop. Insert the query column names as the structure key, and finally append the structure into the root array. Finally, return that array from the function.
<cffunction name="sampleCall" access="remote" returntype="any" returnformat="JSON">
<cfquery name="read" datasource="myDataSource">
SELECT * FROM myTable limit 10
</cfquery>
<cfset myArray = [] >
<cfloop query="read">
<cfset myStr = {} >
<cfset structInsert(myStr, "logid", read.logid)>
<cfset structInsert(myStr, "log_datetime", read.log_datetime)>
<cfset structInsert(myStr, "log_Details", read.log_Details)>
<cfset arrayAppend(myArray,myStr)>
</cfloop>
<cfreturn myArray / >
</cffunction>
Note : The example uses sample columns from my query. You can use your own column names.
If you dump the result like the image below

How to match Google translate result with input string

I want to convert a N number US english to some other languages. I've a US string as "JSON" format like below,
"AdminLocales": {
"-locale": "en_US",
"global": {
"search": "Search",
"noOrdersFound": "No Orders Found",
"prevPage": "Prev Page",
"nextPage": "Next Page"
},
"languageList": {
"1": {
"type": "Student",
"area": "Kovilpatti"
},
"2": {
"type": "Employee",
"area": "Tuticorin"
},
}
}
I want convert almost 3000 string. So I've deserializeJSON my input and loop over the data. Which mean I called the google translation API for each string so my API call will be 3000 times.
So I'm facing request time out issue while call that much time in that API. Like below code
<cfset myKey = 'abcd'>
<cfhttp url="https://translation.googleapis.com/language/translate/v2?key=#myKey#" method="post" charset="utf-8" result="getTokens">
<cfhttpparam type="header" name="Content-Type" value="application/json">
<cfhttpparam type="body" name="data" value="{
'q': 'Welcome',
'target': 'hi'
}">
</cfhttp>
And I found that as we can pass N number of source string input at the same time So I passed it as below
<cfhttpparam type="body" name="data" value="{
'q': 'Welcome',
'q': 'Good Morning',
'q': 'Good Evening',
'q':'Welcome to all',
'q': 'Good night',
'target': 'hi'
}">
And I got the result as the out is same order of input which is I'm passed,
" { "data": { "translations": [ { "translatedText": "स्वागत हे", "detectedSourceLanguage": "en" }, { "translatedText": "शुभ प्रभात", "detectedSourceLanguage": "en" }, { "translatedText": "सुसंध्या", "detectedSourceLanguage": "en" }, { "translatedText": "सभी का स्वागत हैं", "detectedSourceLanguage": "en" }, { "translatedText": "शुभ रात्रि", "detectedSourceLanguage": "en" } ] } } "
This is fine for normal testing.
Now come back to my input, How I can pass the all input at the same time and matched with the result of https call. Becuase I'm not having any sequence order of inputs. I've structure of structure and it's child structure etc... From my input if I pass as "Search,Prev Page,Next Page,Studen,Kovilpatti etc" ( Source string ) it's return the same order. Here How I can match that particular result is for particular source string.
Is Google would expect that you'd pass in a key to identify the correct return ?
Finally I got a solutions for my logic. Here I've explain about how to resolve this kind of scenario. We can achieve this by passing some delimiters. Here I've used - ( Hyphen )as my delimiters.
<cfset myStr = {'Name':'Kannan','State':'Tamilnadu','Country':'India','Position':'Projects Lead','Description':'Test translate API call'}>
This above is my original structure. I've convert this structure key values with hyphen delimeters like below,
<cfset convertString = ''>
<cfloop index='keys' list="#keyList#">
<cfset convertString = listAppend(convertString,structFind(myStr,keys),'-')>
</cfloop>
Now my string looks like Tamilnadu-Kannan-Test translate API call-India-Projects Lead
I can pass the above string in single http call by calling below API,
<cfhttp url="https://translation.googleapis.com/language/translate/v2?key=myApiKey132" method="post" charset="utf-8" result="returnData">
<cfhttpparam type="header" name="Content-Type" value="application/json">
<cfhttpparam type="body" name="data" value="{
'q': '#convertString#',
'target': 'hi'
}">
</cfhttp>
The API call return data like below image,
So my converted string should have my passed delimeter like तमिलनाडु-कन्नन-टेस्ट अनुवाद एपीआई कॉल-इंडिया-प्रोजेक्ट्स लीड
Now I can loop over the result based on my delimeter - ( Hyphen ) and matched my structure key like below code.
<cfset convertStr = {}>
<cfset apiResult = deserializeJSON(returnData.filecontent)>
<cfset a = 1>
<cfloop index="result" list="#apiResult.data.translations[1].translatedText#" delimiters="-">
<cfset structInsert(convertStr,listgetat(keyList,a),result)>
<cfset a = a+1>
</cfloop>
Finally I got my expected result.
Original Structure
After the translation:
<cfdump var="#convertStr#" label="Google Translate Structure" />
Thank you for all attention on these.

ColdFusion 8: Convert OData Response to Array

I'm attempting to update an existing CF8 application to consume and load an array with the results of a newly updated RESTful API response using OData.
Here's the code in question... After pulling the data from the API that responds with the OData jSON string, the code blows up on the last line that inits the loop
<!--- Returned data is in json format so must change to an array. --->
<cfset local.result = deserializeJSON(myResult)>
<!--- Reference the array collection of categories --->
<cfset local.collection = local.result>
<!--- Initialize the output object --->
<cfset local.output = arrayNew(1)>
<!--- Loop over the collection --->
<cfloop from="1" to="#arrayLen(local.collection)#" index="local.arrayIndex">
...
This was working fine using the previous JSON response:
[
{
"id": 1,
"name": "Blah, blah",
}
]
The only change introduced is the updated JSON response:
[
{
"#odata.context": "string",
"value": [
{
"id": 1,
"name": "Blah, blah"
}
]
}
]
I'm sure I'm missing something basic, but I've never worked on CF before so it's new territory here.
Thoughts?
Thanks!
UPDATE:
Apologies on not providing more detail. Here's how the app currently uses the response:
<!--- Loop over the collection --->
<cfloop from="1" to="#arrayLen(local.collection)#" index="local.arrayIndex">
<!--- Create a reference to the array element --->
<cfset local.objectInstance = local.collection[local.arrayIndex]>
<!--- Create a new object reference --->
<cfset local.thisObject = structNew()>
<!--- Seed the object properties --->
<cfset local.thisObject.categoryId = local.objectInstance.id>
<cfset local.thisObject.categoryName = local.objectInstance.name>
<!--- Place the new object in the collection array --->
<cfset arrayAppend(local.output, duplicate(local.thisObject))>
</cfloop>
And here's the error I'm receiving:
Error Occurred While Processing Request
Object of type class coldfusion.runtime.Struct cannot be used as an array
The error occurred in <path to file> line 97
"Line 97" is the begin loop available in the update above:
I did try using the "newJSON" approach offered by Miguel (thank you very much for that!), but unfortunately, I'm running into the same error.
Thanks again!
-Rich
Update after user posted more information
If you are still getting an error then you did something wrong. You must change the way you are referencing the new JSON data object. I created a new Gist using the updated code that you supplied so you can see how it works - TryCF Gist 2
Basically the code within your <cfloop> needs to look like this. Again, notice that there are actually two <cfloop> blocks. This is because the new JSON format generates an array that contains another array.
<!--- Loop over the collection --->
<cfloop from="1" to="#arrayLen(local.collection)#" index="local.arrayIndex">
<cfloop from="1" to="#arrayLen(local.collection[local.arrayIndex].value)#" index="local.arrayIndex2">
<!--- Create a reference to the array element --->
<cfset local.objectInstance = local.collection[local.arrayIndex].value>
<!--- Create a new object reference --->
<cfset local.thisObject = structNew()>
<!--- Seed the object properties --->
<cfset local.thisObject.categoryId = local.objectInstance[local.arrayIndex2].id>
<cfset local.thisObject.categoryName = local.objectInstance[local.arrayIndex2].name>
<!--- Place the new object in the collection array --->
<cfset arrayAppend(local.output, duplicate(local.thisObject))>
</cfloop>
</cfloop>
See the Gist for more details but this assigns the local.output array as it was before. In your original code the local.objectInstance within the loop was a structure. With the new JSON format the local.objectInstance within the loop now contains an array of structures. So you need to reference it as such.
Original answer before question was updated
With the updated JSON you will need to update how your code references the data (which you did not include in your original post). Making some assumptions I can show you how to reference the data using the examples you gave.
First for your original example. Here is some code that would reference and output the data for you. Notice that I have included a <cfdump> tag. You will want to use that in situations like this where you need to see the data. The deserializeJSON() function parses the JSON for you and creates a ColdFusion array of structures.
<cfset oldJSON = '[ { "id": 1, "name": "Blah, blah" } ]'>
<!--- Returned data is in json format so must change to an array. --->
<cfset local.result = deserializeJSON(oldJSON)>
<!--- Reference the array collection of categories --->
<cfset local.collection = local.result>
<!--- Initialize the output object --->
<cfset local.output = arrayNew(1)>
<cfdump var="#local.result#" label="Old JSON">
<!--- Loop over the collection --->
<cfoutput>
<cfloop from="1" to="#arrayLen(local.collection)#" index="local.arrayIndex">
<p>#local.arrayIndex# - #local.collection[local.arrayIndex].id# - #local.collection[local.arrayIndex].name#</p>
</cfloop>
</cfoutput>
That code gives this output:
Here is an example of the updated code needed to retrieve the same values from the new JSON format. Notice that I added another cfloop to reference the data because there are now two arrays.
<cfset newJSON = '[ { "#odata.context": "string", "value": [ { "id": 1, "name": "Blah, blah" } ] } ]'>
<!--- Returned data is in json format so must change to an array. --->
<cfset local.result = deserializeJSON(newJSON)>
<!--- Reference the array collection of categories --->
<cfset local.collection = local.result>
<!--- Initialize the output object --->
<cfset local.output = arrayNew(1)>
<cfdump var="#local.result#" label="New JSON">
<!--- Loop over the collection --->
<cfoutput>
<cfloop from="1" to="#arrayLen(local.collection)#" index="local.arrayIndex">
<cfloop from="1" to="#arrayLen(local.collection[local.arrayIndex].value)#" index="local.arrayIndex2">
<p>#local.arrayIndex# - #local.arrayIndex2# - #local.collection[local.arrayIndex].value[local.arrayIndex2].id# - #local.collection[local.arrayIndex].value[local.arrayIndex2].name#</p>
</cfloop>
</cfloop>
</cfoutput>
That code gives this output:
I created a gist with all of this code that you can play around with - TryCF Gist 1

how to break out a unformatted json in coldfusion

I have a piece of JSON (an example):
["12086012800", ["XXX3220210230310", "XXX3220210260120", "XXX3220210170580", "XXX3220210170220", "XXX3220210270310", "XXX3220210080320", "XXX3220210110020", "XXX3220210220530", "XXX3220210020480", "XXX3220210170210", "XXX3220210230160", "XXX3220210060020", "XXX3220210220160", "XXX3220210110130", "XXX3220210021300", "XXX3220210260220", "XXX3220210260590", "XXX3220210210080", "XXX3220210020740", "XXX3220210020780", "XXX3220210270020", "XXX3220210260020", "XXX3220210230290", "XXX3220210260110", "XXX3220210080080", "XXX3220210110010", "XXX3220210220280", "XXX3220210230280", "XXX3220210021040", "XXX3220210220490", "XXX3220210220170", "XXX3220210230530", "XXX3220210020750", "XXX3220210060280", "XXX3220210230790", "XXX3220210230480", "XXX3220210220460", "XXX3220210260010"],
[{
"lat": 25.91026573627833,
"lng": -80.34233093261719
}, {
"lat": 25.909648111101557,
"lng": -80.34602165222168
}, {
"lat": 25.905015819188293,
"lng": -80.34499168395996
}, {
"lat": 25.904243752850498,
"lng": -80.34310340881348
}, {
"lat": 25.906714347345247,
"lng": -80.34147262573242
}, {
"lat": 25.90972531442551,
"lng": -80.34233093261719
}]
]
It comes through to a cfm page from a $.ajax call:
var save = {
poly: function() {
var value = [];
value.push(GEOID);
value.push($.map(selected, function(el) { return el; }));
value.push(getpaths(poly));
console.log(JSON.stringify(value));
$.ajax({
type: "POST",
url: 'data/geoselect_insert.cfm',
contentType: "application/json; charset=utf-8",
data: JSON.stringify(value),
async: false,
dataType: "json",
success: function (location) {
console.log("saved");
}
});
}
}
The cfm page I need to work is similar to below:
<cfsetting showdebugoutput="yes">
<cfheader name="Content-Type" value="application/json">
<CFPARAM name="cookie.UserID" default="0">
<CFPARAM name="PropertySearchID" default="0">
<cfset ParcelJSON = toString( getHttpRequestData().content ) />
<!--- Double-check to make sure it's a JSON value. --->
<cfif isJSON( ParcelJSON )>
<cfset ParcelJSON2 = DeserializeJSON(ParcelJSON, true) >
<cfoutput>#ParcelJSON2[0]#</cfoutput>
</cfif>
I am assuming that ParcelJSON2 is some form of breakout of ParcelJSON.
I need to split JSON into three variables
<cfset Variable1 = (insert wisdom here) > <--"12086012800",
<cfset Variable2 = (insert wisdom here) > <--["XXX3220210230310","X...]
<cfset Variable3 = (insert wisdom here) > <--["lat":"23.9999"......]
I want to perfrom an cfquery insert to put those values in.
how do I break the incoming JSON without breaking the child JSONs. Each Child JSON is a value of their own.
Any wisdom would be greatly appreciated
Often in this case, I find solace in visualizing my data. Thanks to ColdFusion, it's simple. Directly after your call to DeserializeJSON, dump the data so you can see it.
<cfdump var="#ParcelJSON2#">
At this point you'll see a nicely formatted ColdFusion dump of data that you can get, output and manipulate as you see fit.
<cfset Variable1 = ParcelJSON[1] />
<cfset Variable2 = ParcelJSON[2] />
<cfset Variable3 = ParcelJSON[3] />
If you need to get JSON back out of the CF variables, you can reverse part of the process.
<cfset Variable1 = serializeJSON(ParcelJSON[1]) />
<cfset Variable2 = serializeJSON(ParcelJSON[2]) />
<cfset Variable3 = serializeJSON(ParcelJSON[3]) />
Finally, to see that it's working the way you want it, try dumping again:
<cfdump var="#variables#">
After you have deserialized your json its just ends up combination of arrays and structs. In your case an array of complex objects.
So if the JSON you receive is always in the same format.
I noticed in your example you use Zero to reference the first element in CFML arrays start at One (unlike almost every other language).
So something like:
<cfset Variable1 = ParcelJSON2[1]> <--"12086012800",
<cfset Variable2 = ParcelJSON2[2] > <--["XXX3220210230310","X...]
<cfset Variable3 = ParcelJSON2[3] > <--["lat":"23.9999"......]

how do I change the label for each object in SerializeJSON

I'm trying to use ColdFusion's SerializeJSON() to return JSON data.
So far I have done this:
<cfset rows = [] />
<cfloop query="rsProducts">
<!--- Create a row struct. --->
<cfset row = {} />
<!--- Add each column to our struct. --->
<cfloop
index="column"
list="#rsProducts.columnList#"
delimiters=",">
<cfset row[ column ] = rsProducts[ column ][ rsProducts.currentRow ] />
</cfloop>
<!--- Append the row struct to the row array. --->
<cfset arrayAppend( rows, row ) />
</cfloop>
<cfreturn SerializeJSON(rows, true)>
This works fine and produces JSON like this:
[Object {PRICE: 89.99, PRODUCTSTATUS: 1, COUNTRY: US}, Object {PRICE: 110.50, PRODUCTSTATUS: 4, COUNTRY: UK}, Object {PRICE: 41.20, PRODUCTSTATUS: 1, COUNTRY: IN}]
However instead of a label of "Object" for each item, I'd like it to be "ProductItem" instead. It just makes it easier for me to deal with in jQuery later on.
How could I have it so it labels each object in JSON as "ProductItem"?
You can loop over the data in this manner easily.
.success(function(data) {
var ProductItem = JSON.parse(data);
$.each(ProductItem,function(key,value){
console.log(value.PRICE +" "+ value.PRODUCTSTATUS + " " + value.COUNTRY);
});