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);
});
Related
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.
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": []
};
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
Stuck with the following problem.
In Coldfusion I have a List:
<cfset myList = "Jack,Mike,Tom,Jude,Dick">
I want to convert myList to a Json in precisely this format :
[
{
"name": [
"Jack",
"Mike",
"Tom",
"Jude",
"Dick"
]
}
]
I have been trying with making a Structure and Array and then use serializeJSON, but it's not giving the format I need.
Any suggestions how to get the required Json format from the list?
Many thanks for any help.
Here you go,
<cfset myList = "Jack,Mike,Tom,Jude,Dick">
<cfset jsonRequired = structNew()>
<cfset jsonRequired.name = listToArray(myList) >
<cfset j = serializeJSON(jsonRequired)>
<cfdump var ="#j#">
This will give you required output.
Edit: From comments below
<cfset myList = "Jack,Mike,Tom,Jude,Dick">
<cfset jsonRequired = [{name = listToArray(myList)}]>
<cfset j = serializeJSON(jsonRequired)>
<cfdump var ="#j#">
Let's say I POST this simple JSON payload:
{"foo":null}
On the ColdFusion server, how do I check if the 'foo' property is null?
IsDefined won't work because it will be false for null values. IsNull won't work because IsNull will be true for not just null values, but also for missing properties.
<cfset json = DeserializeJSON(GetHttpRequestData().content) />
<cfdump var="#IsDefined("json.foo")#" /> <!--- false --->
<cfdump var="#IsNull(json.foo)#" /> <!--- true --->
<cfdump var="#IsNull(json.bar)#" /> <!--- true --->
My mistake, I thought null in JSON would be deserialized to empty string, but it's not true.
null in JSON is translated to be struct with key foo but undefined in CF10. (not sure about older CF version)
Therefore, a true isStructValueNull() can be written like this:
function isStructValueNull(struct, key) {
return listFind(structKeyList(struct), key)
&& !structKeyExists(struct, key);
}
json = deserializeJSON('{"foo":null,"bar":123}');
writeDump(isStructValueNull(json, "foo")); // yes
writeDump(isStructValueNull(json, "bar")); // no
or you can loop through json and use structKeyExists(), if it's false, it's null.
function structNullKeyList(struct) {
var nulls = "";
for (var key in struct)
if (!structKeyExists(struct, key))
nulls = listAppend(nulls, key);
return nulls;
}
writeDump(structNullKeyList(json)); // 'foo'
How are you posting this data? What do you get when you dump just the variable "json"?
If you're POSTing data to ColdFusion, each parameter should get converted to a FORM scoped variable. Check the headers of your HTTP POST to see how the data is being sent.
If they're being sent as individual parameters, you should be able to get check <cfif structKeyExists(form, "foo")> and then check to see if the value of form.foo is an empty string. (ColdFusion translates a NULL value to an empty string.)
I use Raillo.
Railo 4.1.3.005 Error (expression)
Message the value from key [FOO] is NULL, which is the same as not existing in CFML.
But we can use Full Null suport. Where it can be properly examined, as you want.
<cfscript>
objectValues = { 'one' : 1 , 'two' : 2 , 'three' : JavaCast( "null", 0 ) , 'four' : null };
dump(objectValues);
// Known existing attribute
dump('three');
dump( isDefined('objectValues.three') );
dump( isNull(objectValues.three) );
dump( StructKeyExists(objectValues,'three') );
// Known Railo Null value
dump('four');
dump( isDefined('objectValues.four') );
dump( isNull(objectValues.four) );
dump( StructKeyExists(objectValues,'four') );
// Unknown existing attribute
dump('five');
dump( isDefined('objectValues.five') );
dump( isNull(objectValues.five) );
dump( StructKeyExists(objectValues,'five') );
</cfscript>
What's your ColdFusion Version?
Actually a true JSON null value should be translated to 'null' by Coldfusion which is equally annoying but manageable
If you store just an empty string in the struct like this:
<cfset s = StructNew()>
<cfset s.test = "" />
<cfset json = SerializeJSON(s)>
<cfdump var="#json#">
<cfset d = DeserializeJSON(json)>
<cfdump var="#d#">
You get the following json: {"TEST":""}
and d.test is ""
But if you explicitly store a java null in the struct like this:
<cfset s = StructNew()>
<cfset s.test = javaCast( "null", "" ) />
<cfset json = serializeJSON(s)>
<cfdump var="#json#">
<cfset d = DeserializeJSON(json)>
<cfdump var="#d#">
you get the following json value: {"TEST":null}
and d.test is 'null'
This is probably more preferable as you could do this:
<cfif StructKeyExists(d,"TEST") AND Compare(d.test,"null") IS 0>