cleanup: remove backup serializer artifacts
This commit is contained in:
@@ -577,6 +577,26 @@
|
|||||||
- синтаксических ошибок в `v1/lib/rest_api_helper.cfc` не обнаружено
|
- синтаксических ошибок в `v1/lib/rest_api_helper.cfc` не обнаружено
|
||||||
- синтаксических ошибок в `v1/lib/expression_parser.cfc` не обнаружено
|
- синтаксических ошибок в `v1/lib/expression_parser.cfc` не обнаружено
|
||||||
|
|
||||||
|
## Обновление: начат консервативный проход по неиспользуемым CFML-файлам
|
||||||
|
|
||||||
|
Дата фиксации: 2026-04-29.
|
||||||
|
|
||||||
|
### Что проверено
|
||||||
|
|
||||||
|
- состав каталогов `v1/resources/bk` и `v1/etc/bk`
|
||||||
|
- наличие явных backup-сериализаторов в `v1/resources/bk`
|
||||||
|
- отсутствие необходимости затрагивать vendor-слой `v1/taffy`
|
||||||
|
|
||||||
|
### Решение по этому этапу
|
||||||
|
|
||||||
|
Сразу удаляется только узкий набор файлов с высокой уверенностью, которые являются backup-артефактами и не участвуют в текущем runtime:
|
||||||
|
- `v1/resources/bk/JSONUtil.cfc`
|
||||||
|
- `v1/resources/bk/JsonBNSerializer.cfc`
|
||||||
|
- `v1/resources/bk/JsonSerializer.cfc`
|
||||||
|
- `v1/resources/bk/JsonUtilSerializer.cfc`
|
||||||
|
|
||||||
|
Каталог `v1/etc/bk` на этом этапе не затрагивается, потому что для его файлов нужна отдельная проверка по назначению и ссылкам.
|
||||||
|
|
||||||
### Следующий мини-этап
|
### Следующий мини-этап
|
||||||
|
|
||||||
Для следующего такого же безопасного прохода выбраны:
|
Для следующего такого же безопасного прохода выбраны:
|
||||||
|
|||||||
@@ -1,620 +0,0 @@
|
|||||||
<!---
|
|
||||||
|
|
||||||
Copyright 2009 Nathan Mische
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|
||||||
--->
|
|
||||||
<!--- 2024-10-05 modified by msyu: date in ISO 8601 with server timezone --->
|
|
||||||
<cfcomponent displayname="JSONUtil" output="false">
|
|
||||||
|
|
||||||
<cfset this.deserializeJSON = deserializeFromJSON />
|
|
||||||
<cfset this.serializeJSON = serializeToJSON />
|
|
||||||
|
|
||||||
<cfset this.deserialize = deserializeFromJSON />
|
|
||||||
<cfset this.serialize = serializeToJSON />
|
|
||||||
|
|
||||||
<cfset this.isLuceeRailo = structKeyExists(server, "lucee") or structKeyExists(server, "railo") />
|
|
||||||
|
|
||||||
<cffunction name="init" output="false">
|
|
||||||
<cfreturn this />
|
|
||||||
</cffunction>
|
|
||||||
|
|
||||||
<cffunction
|
|
||||||
name="deserializeFromJSON"
|
|
||||||
access="public"
|
|
||||||
returntype="any"
|
|
||||||
output="false"
|
|
||||||
hint="Converts a JSON (JavaScript Object Notation) string data representation into CFML data, such as a CFML structure or array.">
|
|
||||||
<cfargument
|
|
||||||
name="JSONVar"
|
|
||||||
type="string"
|
|
||||||
required="true"
|
|
||||||
hint="A string that contains a valid JSON construct, or variable that represents one." />
|
|
||||||
<cfargument
|
|
||||||
name="strictMapping"
|
|
||||||
type="boolean"
|
|
||||||
required="false"
|
|
||||||
default="true"
|
|
||||||
hint="A Boolean value that specifies whether to convert the JSON strictly, as follows:
|
|
||||||
<ul>
|
|
||||||
<li><code>true:</code> (Default) Convert the JSON string to ColdFusion data types that correspond directly to the JSON data types.</li>
|
|
||||||
<li><code>false:</code> Determine if the JSON string contains representations of ColdFusion queries, and if so, convert them to queries.</li>
|
|
||||||
</ul>" />
|
|
||||||
|
|
||||||
<!--- DECLARE VARIABLES --->
|
|
||||||
<cfset var ar = ArrayNew(1) />
|
|
||||||
<cfset var st = StructNew() />
|
|
||||||
<cfset var dataType = "" />
|
|
||||||
<cfset var inQuotes = false />
|
|
||||||
<cfset var startPos = 1 />
|
|
||||||
<cfset var nestingLevel = 0 />
|
|
||||||
<cfset var dataSize = 0 />
|
|
||||||
<cfset var i = 1 />
|
|
||||||
<cfset var skipIncrement = false />
|
|
||||||
<cfset var j = 0 />
|
|
||||||
<cfset var char = "" />
|
|
||||||
<cfset var dataStr = "" />
|
|
||||||
<cfset var structVal = "" />
|
|
||||||
<cfset var structKey = "" />
|
|
||||||
<cfset var colonPos = "" />
|
|
||||||
<cfset var qRows = 0 />
|
|
||||||
<cfset var qCols = "" />
|
|
||||||
<cfset var qCol = "" />
|
|
||||||
<cfset var qData = "" />
|
|
||||||
<cfset var curCharIndex = "" />
|
|
||||||
<cfset var curChar = "" />
|
|
||||||
<cfset var result = "" />
|
|
||||||
<cfset var unescapeVals = "\\,\"",\/,\b,\t,\n,\f,\r" />
|
|
||||||
<cfset var unescapeToVals = "\,"",/,#Chr(8)#,#Chr(9)#,#Chr(10)#,#Chr(12)#,#Chr(13)#" />
|
|
||||||
<cfset var unescapeVals2 = '\,",/,b,t,n,f,r' />
|
|
||||||
<cfset var unescapetoVals2 = '\,",/,#Chr(8)#,#Chr(9)#,#Chr(10)#,#Chr(12)#,#Chr(13)#' />
|
|
||||||
<cfset var dJSONString = "" />
|
|
||||||
<cfset var pos = 0 />
|
|
||||||
|
|
||||||
<cfset var _data = Trim(arguments.JSONVar) />
|
|
||||||
|
|
||||||
<!--- NUMBER --->
|
|
||||||
<cfif IsNumeric(_data)>
|
|
||||||
<cfreturn Val(_data) />
|
|
||||||
|
|
||||||
<!--- NULL --->
|
|
||||||
<cfelseif _data EQ "null">
|
|
||||||
<cfreturn "null" />
|
|
||||||
|
|
||||||
<!--- BOOLEAN --->
|
|
||||||
<cfelseif ListFindNoCase("true,false", _data)>
|
|
||||||
<cfreturn _data />
|
|
||||||
|
|
||||||
<!--- EMPTY STRING --->
|
|
||||||
<cfelseif _data EQ "''" OR _data EQ '""'>
|
|
||||||
<cfreturn "" />
|
|
||||||
|
|
||||||
<!--- STRING --->
|
|
||||||
<cfelseif ReFind('^"[^\\"]*(?:\\.[^\\"]*)*"$', _data) EQ 1 OR ReFind("^'[^\\']*(?:\\.[^\\']*)*'$", _data) EQ 1>
|
|
||||||
<cfset _data = mid(_data, 2, Len(_data)-2) />
|
|
||||||
<!--- If there are any \b, \t, \n, \f, and \r, do extra processing
|
|
||||||
(required because ReplaceList() won't work with those) --->
|
|
||||||
<cfif Find("\b", _data) OR Find("\t", _data) OR Find("\n", _data) OR Find("\f", _data) OR Find("\r", _data)>
|
|
||||||
<cfset curCharIndex = 0 />
|
|
||||||
<cfset curChar = ""/>
|
|
||||||
<cfset dJSONString = ArrayNew(1) />
|
|
||||||
<cfloop condition="true">
|
|
||||||
<cfset curCharIndex = curCharIndex + 1 />
|
|
||||||
<cfif curCharIndex GT len(_data)>
|
|
||||||
<cfbreak />
|
|
||||||
<cfelse>
|
|
||||||
<cfset curChar = mid(_data, curCharIndex, 1) />
|
|
||||||
<cfif curChar EQ "\">
|
|
||||||
<cfset curCharIndex = curCharIndex + 1 />
|
|
||||||
<cfset curChar = mid(_data, curCharIndex,1) />
|
|
||||||
<cfset pos = listFind(unescapeVals2, curChar) />
|
|
||||||
<cfif pos>
|
|
||||||
<cfset ArrayAppend(dJSONString,ListGetAt(unescapetoVals2, pos)) />
|
|
||||||
<cfelse>
|
|
||||||
<cfset ArrayAppend(dJSONString,"\" & curChar) />
|
|
||||||
</cfif>
|
|
||||||
<cfelse>
|
|
||||||
<cfset ArrayAppend(dJSONString,curChar) />
|
|
||||||
</cfif>
|
|
||||||
</cfif>
|
|
||||||
</cfloop>
|
|
||||||
|
|
||||||
<cfreturn ArrayToList(dJSONString,"") />
|
|
||||||
<cfelse>
|
|
||||||
<cfreturn ReplaceList(_data, unescapeVals, unescapeToVals) />
|
|
||||||
</cfif>
|
|
||||||
|
|
||||||
<!--- ARRAY, STRUCT, OR QUERY --->
|
|
||||||
<cfelseif ( Left(_data, 1) EQ "[" AND Right(_data, 1) EQ "]" )
|
|
||||||
OR ( Left(_data, 1) EQ "{" AND Right(_data, 1) EQ "}" )>
|
|
||||||
|
|
||||||
<!--- Store the data type we're dealing with --->
|
|
||||||
<cfif Left(_data, 1) EQ "[" AND Right(_data, 1) EQ "]">
|
|
||||||
<cfset dataType = "array" />
|
|
||||||
<cfelseif ReFindNoCase('^\{"ROWCOUNT":[0-9]+,"COLUMNS":\[("[^"]+",?)+\],"DATA":\{("[^"]+":\[.*\],?)+\}\}$', _data, 0) EQ 1 AND NOT arguments.strictMapping>
|
|
||||||
<cfset dataType = "queryByColumns" />
|
|
||||||
<cfelseif ReFindNoCase('^\{"COLUMNS":\[("[^"]+",?)+\],"DATA":\[(\[.*\],?)+\]\}$', _data, 0) EQ 1 AND NOT arguments.strictMapping>
|
|
||||||
<cfset dataType = "query" />
|
|
||||||
<cfelse>
|
|
||||||
<cfset dataType = "struct" />
|
|
||||||
</cfif>
|
|
||||||
|
|
||||||
<!--- Remove the brackets --->
|
|
||||||
<cfset _data = Trim( Mid(_data, 2, Len(_data)-2) ) />
|
|
||||||
|
|
||||||
<!--- Deal with empty array/struct --->
|
|
||||||
<cfif Len(_data) EQ 0>
|
|
||||||
<cfif dataType EQ "array">
|
|
||||||
<cfreturn ar />
|
|
||||||
<cfelse>
|
|
||||||
<cfreturn st />
|
|
||||||
</cfif>
|
|
||||||
</cfif>
|
|
||||||
|
|
||||||
<!--- Loop through the string characters --->
|
|
||||||
<cfset dataSize = Len(_data) + 1 />
|
|
||||||
<cfloop condition="#i# LTE #dataSize#">
|
|
||||||
<cfset skipIncrement = false />
|
|
||||||
<!--- Save current character --->
|
|
||||||
<cfset char = Mid(_data, i, 1) />
|
|
||||||
|
|
||||||
<!--- If char is a quote, switch the quote status --->
|
|
||||||
<cfif char EQ '"'>
|
|
||||||
<cfset inQuotes = NOT inQuotes />
|
|
||||||
<!--- If char is escape character, skip the next character --->
|
|
||||||
<cfelseif char EQ "\" AND inQuotes>
|
|
||||||
<cfset i = i + 2 />
|
|
||||||
<cfset skipIncrement = true />
|
|
||||||
<!--- If char is a comma and is not in quotes, or if end of string, deal with data --->
|
|
||||||
<cfelseif (char EQ "," AND NOT inQuotes AND nestingLevel EQ 0) OR i EQ Len(_data)+1>
|
|
||||||
<cfset dataStr = Mid(_data, startPos, i-startPos) />
|
|
||||||
|
|
||||||
<!--- If data type is array, append data to the array --->
|
|
||||||
<cfif dataType EQ "array">
|
|
||||||
<cfset arrayappend( ar, deserializeFromJSON(dataStr, arguments.strictMapping) ) />
|
|
||||||
<!--- If data type is struct or query or queryByColumns... --->
|
|
||||||
<cfelseif dataType EQ "struct" OR dataType EQ "query" OR dataType EQ "queryByColumns">
|
|
||||||
<cfset dataStr = Mid(_data, startPos, i-startPos) />
|
|
||||||
<cfset colonPos = Find('":', dataStr) />
|
|
||||||
<cfif colonPos>
|
|
||||||
<cfset colonPos = colonPos + 1 />
|
|
||||||
<cfelse>
|
|
||||||
<cfset colonPos = Find(":", dataStr) />
|
|
||||||
</cfif>
|
|
||||||
<cfset structKey = Trim( Mid(dataStr, 1, colonPos-1) ) />
|
|
||||||
|
|
||||||
<!--- If needed, remove quotes from keys --->
|
|
||||||
<cfif Left(structKey, 1) EQ "'" OR Left(structKey, 1) EQ '"'>
|
|
||||||
<cfset structKey = Mid( structKey, 2, Len(structKey)-2 ) />
|
|
||||||
</cfif>
|
|
||||||
|
|
||||||
<cfset structVal = Mid( dataStr, colonPos+1, Len(dataStr)-colonPos ) />
|
|
||||||
|
|
||||||
<!--- If struct, add to the structure --->
|
|
||||||
<cfif dataType EQ "struct">
|
|
||||||
<cfset StructInsert( st, structKey, deserializeFromJSON(structVal, arguments.strictMapping) ) />
|
|
||||||
|
|
||||||
<!--- If query, build the query --->
|
|
||||||
<cfelseif dataType EQ "queryByColumns">
|
|
||||||
<cfif structKey EQ "rowcount">
|
|
||||||
<cfset qRows = deserializeFromJSON(structVal, arguments.strictMapping) />
|
|
||||||
<cfelseif structKey EQ "columns">
|
|
||||||
<cfset qCols = deserializeFromJSON(structVal, arguments.strictMapping) />
|
|
||||||
<cfset st = QueryNew(ArrayToList(qCols)) />
|
|
||||||
<cfif qRows>
|
|
||||||
<cfset QueryAddRow(st, qRows) />
|
|
||||||
</cfif>
|
|
||||||
<cfelseif structKey EQ "data">
|
|
||||||
<cfset qData = deserializeFromJSON(structVal, arguments.strictMapping) />
|
|
||||||
<cfset ar = StructKeyArray(qData) />
|
|
||||||
<cfloop from="1" to="#ArrayLen(ar)#" index="j">
|
|
||||||
<cfloop from="1" to="#st.recordcount#" index="qRows">
|
|
||||||
<cfset qCol = ar[j] />
|
|
||||||
<cfset QuerySetCell(st, qCol, qData[qCol][qRows], qRows) />
|
|
||||||
</cfloop>
|
|
||||||
</cfloop>
|
|
||||||
</cfif>
|
|
||||||
<cfelseif dataType EQ "query">
|
|
||||||
<cfif structKey EQ "columns">
|
|
||||||
<cfset qCols = deserializeFromJSON(structVal, arguments.strictMapping) />
|
|
||||||
<cfset st = QueryNew(ArrayToList(qCols)) />
|
|
||||||
<cfelseif structKey EQ "data">
|
|
||||||
<cfset qData = deserializeFromJSON(structVal, arguments.strictMapping) />
|
|
||||||
<cfloop from="1" to="#ArrayLen(qData)#" index="qRows">
|
|
||||||
<cfset QueryAddRow(st) />
|
|
||||||
<cfloop from="1" to="#ArrayLen(qCols)#" index="j">
|
|
||||||
<cfset qCol = qCols[j] />
|
|
||||||
<cfset QuerySetCell(st, qCol, qData[qRows][j], qRows) />
|
|
||||||
</cfloop>
|
|
||||||
</cfloop>
|
|
||||||
</cfif>
|
|
||||||
</cfif>
|
|
||||||
</cfif>
|
|
||||||
|
|
||||||
<cfset startPos = i + 1 />
|
|
||||||
<!--- If starting a new array or struct, add to nesting level --->
|
|
||||||
<cfelseif "{[" CONTAINS char AND NOT inQuotes>
|
|
||||||
<cfset nestingLevel = nestingLevel + 1 />
|
|
||||||
<!--- If ending an array or struct, subtract from nesting level --->
|
|
||||||
<cfelseif "]}" CONTAINS char AND NOT inQuotes>
|
|
||||||
<cfset nestingLevel = nestingLevel - 1 />
|
|
||||||
</cfif>
|
|
||||||
|
|
||||||
<cfif NOT skipIncrement>
|
|
||||||
<cfset i = i + 1 />
|
|
||||||
</cfif>
|
|
||||||
</cfloop>
|
|
||||||
|
|
||||||
<!--- Return appropriate value based on data type --->
|
|
||||||
<cfif dataType EQ "array">
|
|
||||||
<cfreturn ar />
|
|
||||||
<cfelse>
|
|
||||||
<cfreturn st />
|
|
||||||
</cfif>
|
|
||||||
|
|
||||||
<!--- INVALID JSON --->
|
|
||||||
<cfelse>
|
|
||||||
<cfthrow message="JSON parsing failure." />
|
|
||||||
</cfif>
|
|
||||||
</cffunction>
|
|
||||||
|
|
||||||
<cffunction
|
|
||||||
name="serializeToJSON"
|
|
||||||
access="public"
|
|
||||||
returntype="string"
|
|
||||||
output="false"
|
|
||||||
hint="Converts ColdFusion data into a JSON (JavaScript Object Notation) representation of the data.">
|
|
||||||
<cfargument
|
|
||||||
name="var"
|
|
||||||
type="any"
|
|
||||||
required="true"
|
|
||||||
hint="A ColdFusion data value or variable that represents one." />
|
|
||||||
<cfargument
|
|
||||||
name="serializeQueryByColumns"
|
|
||||||
type="boolean"
|
|
||||||
required="false"
|
|
||||||
default="false"
|
|
||||||
hint="A Boolean value that specifies how to serialize ColdFusion queries.
|
|
||||||
<ul>
|
|
||||||
<li><code>false</code>: (Default) Creates an object with two entries: an array of column names and an array of row arrays. This format is required by the HTML format cfgrid tag.</li>
|
|
||||||
<li><code>true</code>: Creates an object that corresponds to WDDX query format.</li>
|
|
||||||
</ul>">
|
|
||||||
<cfargument
|
|
||||||
name="strictMapping"
|
|
||||||
type="boolean"
|
|
||||||
required="false"
|
|
||||||
default="false"
|
|
||||||
hint="A Boolean value that specifies whether to convert the ColdFusion data strictly, as follows:
|
|
||||||
<ul>
|
|
||||||
<li><code>false:</code> (Default) Convert the ColdFusion data to a JSON string using ColdFusion data types.</li>
|
|
||||||
<li><code>true:</code> Convert the ColdFusion data to a JSON string using underlying Java/SQL data types.</li>
|
|
||||||
</ul>" />
|
|
||||||
|
|
||||||
<!--- VARIABLE DECLARATION --->
|
|
||||||
<cfset var jsonString = "" />
|
|
||||||
<cfset var tempVal = "" />
|
|
||||||
<cfset var arKeys = "" />
|
|
||||||
<cfset var colPos = 1 />
|
|
||||||
<cfset var md = "" />
|
|
||||||
<cfset var rowDel = "" />
|
|
||||||
<cfset var colDel = "" />
|
|
||||||
<cfset var className = "" />
|
|
||||||
<cfset var i = 1 />
|
|
||||||
<cfset var column = "" />
|
|
||||||
<cfset var datakey = "" />
|
|
||||||
<cfset var recordcountkey = "" />
|
|
||||||
<cfset var columnlist = "" />
|
|
||||||
<cfset var columnlistkey = "" />
|
|
||||||
<cfset var columnJavaTypes = "" />
|
|
||||||
<cfset var dJSONString = "" />
|
|
||||||
<cfset var escapeToVals = "\\,\"",\/,\b,\t,\n,\f,\r" />
|
|
||||||
<cfset var escapeVals = "\,"",/,#Chr(8)#,#Chr(9)#,#Chr(10)#,#Chr(12)#,#Chr(13)#" />
|
|
||||||
|
|
||||||
<cfset var _data = arguments.var />
|
|
||||||
|
|
||||||
<cfif arguments.strictMapping>
|
|
||||||
<!--- GET THE CLASS NAME --->
|
|
||||||
<cfset className = getClassName(_data) />
|
|
||||||
</cfif>
|
|
||||||
|
|
||||||
<!--- TRY STRICT MAPPING --->
|
|
||||||
|
|
||||||
<cfif Len(className) AND CompareNoCase(className,"java.lang.String") eq 0>
|
|
||||||
<cfreturn '"' & ReplaceList(_data, escapeVals, escapeToVals) & '"' />
|
|
||||||
|
|
||||||
<cfelseif Len(className) AND CompareNoCase(className,"java.lang.Boolean") eq 0>
|
|
||||||
<cfreturn ReplaceList(ToString(_data), 'YES,NO', 'true,false') />
|
|
||||||
|
|
||||||
<cfelseif Len(className) AND CompareNoCase(className,"java.lang.Integer") eq 0>
|
|
||||||
<cfreturn ToString(_data) />
|
|
||||||
|
|
||||||
<cfelseif Len(className) AND CompareNoCase(className,"java.lang.Long") eq 0>
|
|
||||||
<cfreturn ToString(_data) />
|
|
||||||
|
|
||||||
<cfelseif Len(className) AND CompareNoCase(className,"java.lang.Float") eq 0>
|
|
||||||
<cfreturn ToString(_data) />
|
|
||||||
|
|
||||||
<cfelseif Len(className) AND CompareNoCase(className,"java.lang.Double") eq 0>
|
|
||||||
<cfreturn ToString(_data) />
|
|
||||||
|
|
||||||
<!--- BINARY --->
|
|
||||||
<cfelseif IsBinary(_data)>
|
|
||||||
<cfthrow message="JSON serialization failure: Unable to serialize binary data to JSON." />
|
|
||||||
|
|
||||||
<!--- BOOLEAN --->
|
|
||||||
<cfelseif IsBoolean(_data) AND NOT IsNumeric(_data)>
|
|
||||||
<cfreturn ReplaceList(YesNoFormat(_data), 'Yes,No', 'true,false') />
|
|
||||||
|
|
||||||
<!--- NUMBER --->
|
|
||||||
<cfelseif IsNumeric(_data)>
|
|
||||||
<cfif getClassName(_data) eq "java.lang.String">
|
|
||||||
<cfreturn Val(_data).toString() />
|
|
||||||
<cfelse>
|
|
||||||
<cfreturn _data.toString() />
|
|
||||||
</cfif>
|
|
||||||
|
|
||||||
<!--- DATE --->
|
|
||||||
<cfelseif IsDate(_data)>
|
|
||||||
<!--- <cfreturn '"#DateFormat(_data, "mmmm, dd yyyy")# #TimeFormat(_data, "HH:mm:ss")#"' /> --->
|
|
||||||
<!--- // Write the date in ISO 8601 time string format. We're going to assume that the
|
|
||||||
// date is already in the desired timezone.
|
|
||||||
///writeOutput( """" & dateFormat( input, "yyyy-mm-dd" ) & "T" & timeFormat( input, "HH:mm:ss.l" ) & "Z""" );
|
|
||||||
//2024-06-27 modified by msyu: added server timezone --->
|
|
||||||
<cfreturn """" & dateFormat( _data, "yyyy-mm-dd" ) & "T" & timeFormat( _data, "HH:mm:ss.lXX" ) & """" />
|
|
||||||
|
|
||||||
<!--- STRING --->
|
|
||||||
<cfelseif IsSimpleValue(_data)>
|
|
||||||
<!---<cfreturn '"' & ReplaceList(_data, escapeVals, escapeToVals) & '"' />--->
|
|
||||||
<cfreturn writeJsonUtf8String(_data) />
|
|
||||||
|
|
||||||
<!--- RAILO XML --->
|
|
||||||
<cfelseif this.isLuceeRailo and IsXML(_data)>
|
|
||||||
<cfreturn '"' & ReplaceList(ToString(_data), escapeVals, escapeToVals) & '"' />
|
|
||||||
|
|
||||||
<!--- CUSTOM FUNCTION --->
|
|
||||||
<cfelseif IsCustomFunction(_data)>
|
|
||||||
<cfreturn serializeToJSON( GetMetadata(_data), arguments.serializeQueryByColumns, arguments.strictMapping) />
|
|
||||||
|
|
||||||
<!--- OBJECT --->
|
|
||||||
<cfelseif IsObject(_data)>
|
|
||||||
<cfreturn "{}" />
|
|
||||||
|
|
||||||
<!--- ARRAY --->
|
|
||||||
<cfelseif IsArray(_data)>
|
|
||||||
<cfset dJSONString = ArrayNew(1) />
|
|
||||||
<cfloop from="1" to="#ArrayLen(_data)#" index="i">
|
|
||||||
<cfset tempVal = serializeToJSON( _data[i], arguments.serializeQueryByColumns, arguments.strictMapping ) />
|
|
||||||
<cfset ArrayAppend(dJSONString,tempVal) />
|
|
||||||
</cfloop>
|
|
||||||
|
|
||||||
<cfreturn "[" & ArrayToList(dJSONString,",") & "]" />
|
|
||||||
|
|
||||||
<!--- STRUCT --->
|
|
||||||
<cfelseif IsStruct(_data)>
|
|
||||||
<cfset dJSONString = ArrayNew(1) />
|
|
||||||
<cfset arKeys = StructKeyArray(_data) />
|
|
||||||
<cfloop from="1" to="#ArrayLen(arKeys)#" index="i">
|
|
||||||
<cfif IsDefined("_data.#arKeys[i]#") >
|
|
||||||
<cfset tempVal = serializeToJSON(_data[ arKeys[i] ], arguments.serializeQueryByColumns, arguments.strictMapping ) />
|
|
||||||
<cfelse>
|
|
||||||
<cfset tempVal = "null" />
|
|
||||||
</cfif>
|
|
||||||
<cfset ArrayAppend(dJSONString,'"' & arKeys[i] & '":' & tempVal) />
|
|
||||||
</cfloop>
|
|
||||||
|
|
||||||
<cfreturn "{" & ArrayToList(dJSONString,",") & "}" />
|
|
||||||
|
|
||||||
<!--- QUERY --->
|
|
||||||
<cfelseif IsQuery(_data)>
|
|
||||||
<cfset dJSONString = ArrayNew(1) />
|
|
||||||
|
|
||||||
<!--- Add query meta data --->
|
|
||||||
<cfset recordcountKey = "ROWCOUNT" />
|
|
||||||
<cfset columnlistKey = "COLUMNS" />
|
|
||||||
<cfset columnlist = "" />
|
|
||||||
<cfset dataKey = "DATA" />
|
|
||||||
<cfset md = GetMetadata(_data) />
|
|
||||||
<cfset columnJavaTypes = StructNew() />
|
|
||||||
<cfloop from="1" to="#ArrayLen(md)#" index="column">
|
|
||||||
<cfset columnlist = ListAppend(columnlist,UCase(md[column].Name),',') />
|
|
||||||
<cfif StructKeyExists(md[column],"TypeName")>
|
|
||||||
<cfset columnJavaTypes[md[column].Name] = getJavaType(md[column].TypeName) />
|
|
||||||
<cfelse>
|
|
||||||
<cfset columnJavaTypes[md[column].Name] = "" />
|
|
||||||
</cfif>
|
|
||||||
</cfloop>
|
|
||||||
|
|
||||||
<cfif arguments.serializeQueryByColumns>
|
|
||||||
<cfset ArrayAppend(dJSONString,'"#recordcountKey#":' & _data.recordcount) />
|
|
||||||
<cfset ArrayAppend(dJSONString,',"#columnlistKey#":[' & ListQualify(columnlist, '"') & ']') />
|
|
||||||
<cfset ArrayAppend(dJSONString,',"#dataKey#":{') />
|
|
||||||
<cfset colDel = "">
|
|
||||||
<cfloop list="#columnlist#" delimiters="," index="column">
|
|
||||||
<cfset ArrayAppend(dJSONString,colDel) />
|
|
||||||
<cfset ArrayAppend(dJSONString,'"#column#":[') />
|
|
||||||
<cfset rowDel = "">
|
|
||||||
<cfloop from="1" to="#_data.recordcount#" index="i">
|
|
||||||
<cfset ArrayAppend(dJSONString,rowDel) />
|
|
||||||
<cfif (arguments.strictMapping or this.isLuceeRailo) AND Len(columnJavaTypes[column])>
|
|
||||||
<cfset tempVal = serializeToJSON( JavaCast(columnJavaTypes[column],_data[column][i]), arguments.serializeQueryByColumns, arguments.strictMapping ) />
|
|
||||||
<cfelse>
|
|
||||||
<cfset tempVal = serializeToJSON( _data[column][i], arguments.serializeQueryByColumns, arguments.strictMapping ) />
|
|
||||||
</cfif>
|
|
||||||
<cfset ArrayAppend(dJSONString,tempVal) />
|
|
||||||
<cfset rowDel = ",">
|
|
||||||
</cfloop>
|
|
||||||
<cfset ArrayAppend(dJSONString,']') />
|
|
||||||
<cfset colDel = ",">
|
|
||||||
</cfloop>
|
|
||||||
<cfset ArrayAppend(dJSONString,'}') />
|
|
||||||
<cfelse>
|
|
||||||
<cfset ArrayAppend(dJSONString,'"#columnlistKey#":[' & ListQualify(columnlist, '"') & ']') />
|
|
||||||
<cfset ArrayAppend(dJSONString,',"#dataKey#":[') />
|
|
||||||
<cfset rowDel = "">
|
|
||||||
<cfloop from="1" to="#_data.recordcount#" index="i">
|
|
||||||
<cfset ArrayAppend(dJSONString,rowDel) />
|
|
||||||
<cfset ArrayAppend(dJSONString,'[') />
|
|
||||||
<cfset colDel = "">
|
|
||||||
<cfloop list="#columnlist#" delimiters="," index="column">
|
|
||||||
<cfset ArrayAppend(dJSONString,colDel) />
|
|
||||||
<cfif (arguments.strictMapping or this.isLuceeRailo) AND Len(columnJavaTypes[column])>
|
|
||||||
<cfset tempVal = serializeToJSON( JavaCast(columnJavaTypes[column],_data[column][i]), arguments.serializeQueryByColumns, arguments.strictMapping ) />
|
|
||||||
<cfelse>
|
|
||||||
<cfset tempVal = serializeToJSON( _data[column][i], arguments.serializeQueryByColumns, arguments.strictMapping ) />
|
|
||||||
</cfif>
|
|
||||||
<cfset ArrayAppend(dJSONString,tempVal) />
|
|
||||||
<cfset colDel=","/>
|
|
||||||
</cfloop>
|
|
||||||
<cfset ArrayAppend(dJSONString,']') />
|
|
||||||
<cfset rowDel = "," />
|
|
||||||
</cfloop>
|
|
||||||
<cfset ArrayAppend(dJSONString,']') />
|
|
||||||
</cfif>
|
|
||||||
|
|
||||||
<cfreturn "{" & ArrayToList(dJSONString,"") & "}">
|
|
||||||
|
|
||||||
<!--- XML --->
|
|
||||||
<cfelseif IsXML(_data)>
|
|
||||||
<cfreturn '"' & ReplaceList(ToString(_data), escapeVals, escapeToVals) & '"' />
|
|
||||||
|
|
||||||
|
|
||||||
<!--- UNKNOWN OBJECT TYPE --->
|
|
||||||
<cfelse>
|
|
||||||
<cfreturn "{}" />
|
|
||||||
</cfif>
|
|
||||||
|
|
||||||
</cffunction>
|
|
||||||
|
|
||||||
<cffunction
|
|
||||||
name="getJavaType"
|
|
||||||
access="private"
|
|
||||||
returntype="string"
|
|
||||||
output="false"
|
|
||||||
hint="Maps SQL to Java types. Returns blank string for unhandled SQL types.">
|
|
||||||
<cfargument
|
|
||||||
name="sqlType"
|
|
||||||
type="string"
|
|
||||||
required="true"
|
|
||||||
hint="A SQL datatype." />
|
|
||||||
|
|
||||||
<cfswitch expression="#arguments.sqlType#">
|
|
||||||
|
|
||||||
<cfcase value="bit">
|
|
||||||
<cfreturn "boolean" />
|
|
||||||
</cfcase>
|
|
||||||
|
|
||||||
<cfcase value="tinyint,smallint,integer">
|
|
||||||
<cfreturn "int" />
|
|
||||||
</cfcase>
|
|
||||||
|
|
||||||
<cfcase value="bigint">
|
|
||||||
<cfreturn "long" />
|
|
||||||
</cfcase>
|
|
||||||
|
|
||||||
<cfcase value="real,float">
|
|
||||||
<cfreturn "float" />
|
|
||||||
</cfcase>
|
|
||||||
|
|
||||||
<cfcase value="double">
|
|
||||||
<cfreturn "double" />
|
|
||||||
</cfcase>
|
|
||||||
|
|
||||||
<cfcase value="char,varchar,longvarchar">
|
|
||||||
<cfreturn "string" />
|
|
||||||
</cfcase>
|
|
||||||
|
|
||||||
<cfdefaultcase>
|
|
||||||
<cfreturn "" />
|
|
||||||
</cfdefaultcase>
|
|
||||||
|
|
||||||
</cfswitch>
|
|
||||||
|
|
||||||
</cffunction>
|
|
||||||
|
|
||||||
<cffunction
|
|
||||||
name="getClassName"
|
|
||||||
access="private"
|
|
||||||
returntype="string"
|
|
||||||
output="false"
|
|
||||||
hint="Returns a variable's underlying java Class name.">
|
|
||||||
<cfargument
|
|
||||||
name="data"
|
|
||||||
type="any"
|
|
||||||
required="true"
|
|
||||||
hint="A variable." />
|
|
||||||
|
|
||||||
<!--- GET THE CLASS NAME --->
|
|
||||||
<cftry>
|
|
||||||
<cfreturn arguments.data.getClass().getName() />
|
|
||||||
<cfcatch type="any">
|
|
||||||
<cfreturn "" />
|
|
||||||
</cfcatch>
|
|
||||||
</cftry>
|
|
||||||
|
|
||||||
</cffunction>
|
|
||||||
|
|
||||||
<cffunction
|
|
||||||
name="writeJsonUtf8String"
|
|
||||||
access="private"
|
|
||||||
returntype="string"
|
|
||||||
output="false"
|
|
||||||
hint="Returns a variable's underlying java Class name.">
|
|
||||||
<cfargument
|
|
||||||
name="data"
|
|
||||||
type="string"
|
|
||||||
required="true"
|
|
||||||
hint="A string to serialze." />
|
|
||||||
|
|
||||||
<!--- GET THE CLASS NAME --->
|
|
||||||
<cfset var json = '"' />
|
|
||||||
<cfset var end = Len(data) - 1 />
|
|
||||||
<cfset var i = 0 />
|
|
||||||
<cfset var c = "" />
|
|
||||||
<cfset var integer = CreateObject("java","java.lang.Integer") />
|
|
||||||
<cfset var pad = "" />
|
|
||||||
<cfset var hex = "" />
|
|
||||||
|
|
||||||
<cfloop from="0" to="#end#" index="i">
|
|
||||||
<cfset c = data.charAt(i) />
|
|
||||||
<cfif c lt ' '>
|
|
||||||
<cfif c eq Chr(8)>
|
|
||||||
<cfset json = json & "\b" />
|
|
||||||
<cfelseif c eq Chr(9)>
|
|
||||||
<cfset json = json & "\t" />
|
|
||||||
<cfelseif c eq Chr(10)>
|
|
||||||
<cfset json = json & "\n" />
|
|
||||||
<cfelseif c eq Chr(12)>
|
|
||||||
<cfset json = json & "\f" />
|
|
||||||
<cfelseif c eq Chr(13)>
|
|
||||||
<cfset json = json & "\r" />
|
|
||||||
<cfelse>
|
|
||||||
<cfset hex = integer.toHexString(c) />
|
|
||||||
<cfset json = json & "\u" />
|
|
||||||
<cfset pad = 4 - Len(hex) />
|
|
||||||
<cfset json = json & RepeatString("0", pad) />
|
|
||||||
<cfset json = json & hex />
|
|
||||||
</cfif>
|
|
||||||
<cfelseif c eq '\' or c eq '/' or c eq '"'>
|
|
||||||
<cfset json = json & "\" & c />
|
|
||||||
<cfelse>
|
|
||||||
<cfset json = json & c />
|
|
||||||
</cfif>
|
|
||||||
</cfloop>
|
|
||||||
<cfset json = json & '"' />
|
|
||||||
|
|
||||||
<cfreturn json />
|
|
||||||
|
|
||||||
</cffunction>
|
|
||||||
|
|
||||||
</cfcomponent>
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
<cfcomponent extends="taffy.core.baseSerializer">
|
|
||||||
|
|
||||||
<cffunction
|
|
||||||
name="getAsJson"
|
|
||||||
output="false"
|
|
||||||
taffy:mime="application/json"
|
|
||||||
taffy:default="true">
|
|
||||||
<cfreturn variables.jsonSerializer.serialize(variables.data) />
|
|
||||||
</cffunction>
|
|
||||||
|
|
||||||
<cffunction name="setJsonSerializer" output="false">
|
|
||||||
<cfargument name="jsonSerializer" required="true" />
|
|
||||||
<cfset variables.jsonSerializer = arguments.jsonSerializer />
|
|
||||||
<!--- Уже здесь непонятно, как это работает. Похоже на dependency injection?
|
|
||||||
Почему не вызывается init? --->
|
|
||||||
<cfset variables.jsonSerializer.init()/>
|
|
||||||
</cffunction>
|
|
||||||
|
|
||||||
</cfcomponent>
|
|
||||||
@@ -1,519 +0,0 @@
|
|||||||
component
|
|
||||||
extends="taffy.core.baseSerializer"
|
|
||||||
output = false
|
|
||||||
hint = "I provide a way to serialize complex ColdFusion data values as case-sensitive JavaScript Object Notation (JSON) strings."
|
|
||||||
{
|
|
||||||
//2024-06-27 modified by msyu: added server timezone
|
|
||||||
//2024-10-05 modified by msyu: null fix from JSONUtils (yet still no luck with case sens and date format).Вспомнил, что этому сериализатору нужно указывать тип каждого поля
|
|
||||||
|
|
||||||
// I return the initialized component.
|
|
||||||
public any function init() {
|
|
||||||
|
|
||||||
// Every key is added to the full key list - used for one-time key serialization.
|
|
||||||
fullKeyList = {};
|
|
||||||
|
|
||||||
// Every key is added to the hint list so that we don't have to switch on the lists.
|
|
||||||
fullHintList = {};
|
|
||||||
|
|
||||||
// These key lists determine special data serialization.
|
|
||||||
stringKeyList = {};
|
|
||||||
booleanKeyList = {};
|
|
||||||
integerKeyList = {};
|
|
||||||
floatKeyList = {};
|
|
||||||
dateKeyList = {};
|
|
||||||
|
|
||||||
// These keys will NOT be used in serialization (ie. the key/value pairs will not be
|
|
||||||
// added to the serialized output).
|
|
||||||
blockedKeyList = {};
|
|
||||||
|
|
||||||
// Return the initialized component.
|
|
||||||
return( this );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ---
|
|
||||||
// PUBLIC METHODS.
|
|
||||||
// ---
|
|
||||||
|
|
||||||
|
|
||||||
// I define the given key without a type. This is here to provide key-casing without caring
|
|
||||||
// about why type of data conversion takes place. Returns serializer.
|
|
||||||
public any function asAny( required string key ) {
|
|
||||||
|
|
||||||
return( defineKey( fullKeyList, key, "any" ) );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// I define the given key as a boolean. Returns serializer.
|
|
||||||
public any function asBoolean( required string key ) {
|
|
||||||
|
|
||||||
return( defineKey( booleanKeyList, key, "boolean" ) );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// I define the given key as a date. Returns serializer.
|
|
||||||
public any function asDate( required string key ) {
|
|
||||||
|
|
||||||
return( defineKey( dateKeyList, key, "date" ) );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// I define the given key as a float / decimal. Returns serializer.
|
|
||||||
public any function asFloat( required string key ) {
|
|
||||||
|
|
||||||
return( defineKey( floatKeyList, key, "float" ) );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// I define the given key as an integer. Returns serializer.
|
|
||||||
public any function asInteger( required string key ) {
|
|
||||||
|
|
||||||
return( defineKey( integerKeyList, key, "integer" ) );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// I define the given key as a string. Returns serializer.
|
|
||||||
public any function asString( required string key ) {
|
|
||||||
|
|
||||||
return( defineKey( stringKeyList, key, "string" ) );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// I define the key as one that should not be included in the serialized response.
|
|
||||||
public any function exclude( required string key ) {
|
|
||||||
|
|
||||||
blockedKeyList[ key ] = true;
|
|
||||||
|
|
||||||
return( this );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* I serialize the given input as JavaScript Object Notation (JSON) using the case-sensitive
|
|
||||||
* values defined in the key-list.
|
|
||||||
*
|
|
||||||
* @output false
|
|
||||||
*/
|
|
||||||
public string function serialize( required any input ) {
|
|
||||||
|
|
||||||
// Write the serialized value to the output buffer.
|
|
||||||
savecontent variable = "local.serializedInput" {
|
|
||||||
|
|
||||||
serializeInput( input, "any" );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return( serializedInput );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ---
|
|
||||||
// PRIVATE METHODS.
|
|
||||||
// ---
|
|
||||||
|
|
||||||
|
|
||||||
// I define the given key within the given key list.
|
|
||||||
private any function defineKey(
|
|
||||||
required struct keyList,
|
|
||||||
required string key,
|
|
||||||
required string hint
|
|
||||||
) {
|
|
||||||
|
|
||||||
if ( structKeyExists( fullKeyList, key ) ) {
|
|
||||||
|
|
||||||
throw(
|
|
||||||
type = "DuplicateKey",
|
|
||||||
message = "The key [#key#] has already been defined within the serializer.",
|
|
||||||
detail = "The current key list is: #structKeyList( fullKeyList, ', ' )#"
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add to the appropriate data-type lists. This one is used for existence checking.
|
|
||||||
keyList[ key ] = key;
|
|
||||||
|
|
||||||
// Add all keys to the full key list as well. This one is used to store the serialization
|
|
||||||
// of the key so that it doesn't have to be recalculated each time the object is serialized.
|
|
||||||
fullKeyList[ key ] = serializeString( key );
|
|
||||||
|
|
||||||
// If we have a specific type, then add the hint to the full hint list as well. This will
|
|
||||||
// allow us to quickly look up the pass-through data type hint during serialization.
|
|
||||||
// --
|
|
||||||
// NOTE: The reason we don't want to pass through "any" is that we want parent types to be
|
|
||||||
// able to "fall through" during the object traversal. If we added "any" to the type list,
|
|
||||||
// then it would always overwrite the parent data type.
|
|
||||||
if ( hint != "any" ) {
|
|
||||||
|
|
||||||
fullHintList[ key ] = hint;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return this reference for method chaining.
|
|
||||||
return( this );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// I walk the given object, writing the serialized value to the output (which is expected to
|
|
||||||
// be a content buffer).
|
|
||||||
// ---
|
|
||||||
// NOTE: THIS METHOD IS HUGE - this is on purpose. Since serialization is a rather intense
|
|
||||||
// process, I am trying to cut out as much overhead as possible. In this case, we're cutting
|
|
||||||
// out extra stack space by inlining and duplicating a lot of functionality. This is being done
|
|
||||||
// at the COST of clarity and non-repetitive code.
|
|
||||||
private void function serializeInput(
|
|
||||||
required any input,
|
|
||||||
required string hint
|
|
||||||
) {
|
|
||||||
|
|
||||||
// Serialize the data base on the type of input. We are organizing this in terms of the
|
|
||||||
// most commonly-used values first. The anticipation is that the vast majority of data
|
|
||||||
// types will be simple values.
|
|
||||||
if ( isSimpleValue( input ) ) {
|
|
||||||
|
|
||||||
if ( ( hint == "string" ) || ( hint == "any" ) ) {
|
|
||||||
|
|
||||||
// If the string appears to be numeric, then we have to prefix it to make sure
|
|
||||||
// ColdFusion doesn't accidentally convert it to a number.
|
|
||||||
if ( isNumeric( input ) ) {
|
|
||||||
|
|
||||||
writeOutput( """" & input & """" );
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
serializeInputString( input );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if ( ( hint == "boolean" ) && isBoolean( input ) ) {
|
|
||||||
|
|
||||||
writeOutput( input ? "true" : "false" );
|
|
||||||
|
|
||||||
} else if ( ( ( hint == "integer" ) || ( hint == "float" ) ) && isNumeric( input ) ) {
|
|
||||||
|
|
||||||
writeOutput( input );
|
|
||||||
|
|
||||||
} else if ( ( ( hint == "integer" ) || ( hint == "float" ) ) && isBoolean( input ) ) {
|
|
||||||
|
|
||||||
writeOutput( input ? "1" : "0" );
|
|
||||||
|
|
||||||
} else if ( ( hint == "date" ) && ( isDate( input ) || isNumericDate( input ) ) ) {
|
|
||||||
|
|
||||||
// Write the date in ISO 8601 time string format. We're going to assume that the
|
|
||||||
// date is already in the desired timezone.
|
|
||||||
///writeOutput( """" & dateFormat( input, "yyyy-mm-dd" ) & "T" & timeFormat( input, "HH:mm:ss.l" ) & "Z""" );
|
|
||||||
//2024-06-27 modified by msyu: added server timezone
|
|
||||||
writeOutput( """" & dateFormat( input, "yyyy-mm-dd" ) & "T" & timeFormat( input, "HH:mm:ss.lXX" ) & """" );
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
serializeInputString( input );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
} // END: isSimpleValue().
|
|
||||||
|
|
||||||
|
|
||||||
// I'm expecting the struct to be the next most common data type since it will likely be
|
|
||||||
// the container for the majority of data values.
|
|
||||||
if ( isStruct( input ) ) {
|
|
||||||
|
|
||||||
writeOutput( "{" );
|
|
||||||
|
|
||||||
var isFirst = true;
|
|
||||||
|
|
||||||
for ( var key in input ) {
|
|
||||||
|
|
||||||
// Skip any black-listed keys.
|
|
||||||
if ( structKeyExists( blockedKeyList, key ) ) {
|
|
||||||
|
|
||||||
continue;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle the item delimiter.
|
|
||||||
if ( isFirst ) {
|
|
||||||
|
|
||||||
isFirst = false;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
writeOutput( "," );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure that the given key can be referenced on the full-key list. This way,
|
|
||||||
// the subsequent logic will be easier.
|
|
||||||
if ( ! structKeyExists( fullKeyList, key ) ) {
|
|
||||||
|
|
||||||
asAny( lcase( key ) );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
writeOutput( fullKeyList[ key ] & ":" );
|
|
||||||
|
|
||||||
// Pass in the most appropriate data-type hint based on the parent key.
|
|
||||||
if ( structKeyExists( fullHintList, key ) ) {
|
|
||||||
|
|
||||||
serializeInput( input[ key ], fullHintList[ key ] );
|
|
||||||
|
|
||||||
// If the given key is unknown, just pass through the most recent hint as
|
|
||||||
// it may be defining the type for an entire structure.
|
|
||||||
} else {
|
|
||||||
// patch for null, from JSONUtils
|
|
||||||
if (IsDefined("input.#key#")) {
|
|
||||||
serializeInput( input[ key ], hint);
|
|
||||||
} else {
|
|
||||||
writeOutput("null") ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
writeOutput( "}" );
|
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
} // END: isStruct().
|
|
||||||
|
|
||||||
|
|
||||||
if ( isArray( input ) ) {
|
|
||||||
|
|
||||||
writeOutput( "[" );
|
|
||||||
|
|
||||||
var isFirst = true;
|
|
||||||
|
|
||||||
// Handle the item delimiter.
|
|
||||||
for ( var value in input ) {
|
|
||||||
|
|
||||||
if ( isFirst ) {
|
|
||||||
|
|
||||||
isFirst = false;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
writeOutput( "," );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Since we don't have a key to go off of, pass-through the most recent hint.
|
|
||||||
serializeInput( value, hint );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
writeOutput( "]" );
|
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
} // END: isArray().
|
|
||||||
|
|
||||||
|
|
||||||
// When we serialize a query, we're going to treat it like an array of structs.
|
|
||||||
if ( isQuery( input ) ) {
|
|
||||||
|
|
||||||
var keys = listToArray( input.columnList );
|
|
||||||
|
|
||||||
// Make sure each column is defined as a known key - makes the subsequent logic easier.
|
|
||||||
for ( var key in keys ) {
|
|
||||||
|
|
||||||
if ( ! structKeyExists( fullKeyList, key ) ) {
|
|
||||||
|
|
||||||
asAny( lcase( key ) );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
writeOutput( "[" );
|
|
||||||
|
|
||||||
// Serialize each row of the query as a struct.
|
|
||||||
for ( var i = 1 ; i <= input.recordCount ; i++ ) {
|
|
||||||
|
|
||||||
// Handle the row delimiter.
|
|
||||||
if ( i > 1 ) {
|
|
||||||
|
|
||||||
writeOutput( "," );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
writeOutput( "{" );
|
|
||||||
|
|
||||||
var isFirst = true;
|
|
||||||
|
|
||||||
for ( var key in keys ) {
|
|
||||||
|
|
||||||
// Skip any black-listed keys.
|
|
||||||
if ( structKeyExists( blockedKeyList, key ) ) {
|
|
||||||
|
|
||||||
continue;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle the item delimiter (in the current row).
|
|
||||||
if ( isFirst ) {
|
|
||||||
|
|
||||||
isFirst = false;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
writeOutput( "," );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
writeOutput( fullKeyList[ key ] & ":" );
|
|
||||||
|
|
||||||
// Pass in the most appropriate data-type hint based on the parent key.
|
|
||||||
if ( structKeyExists( fullHintList, key ) ) {
|
|
||||||
|
|
||||||
serializeInput( input[ key ][ i ], fullHintList[ key ] );
|
|
||||||
|
|
||||||
// If the given key is unknown, just pass through the most recent hint as
|
|
||||||
// it may be defining the type for an entire structure.
|
|
||||||
} else {
|
|
||||||
|
|
||||||
serializeInput( input[ key ][ i ], hint );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
} // END: Key list.
|
|
||||||
|
|
||||||
writeOutput( "}" );
|
|
||||||
|
|
||||||
} // END: Row list.
|
|
||||||
|
|
||||||
writeOutput( "]" );
|
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
} // END: isQuery().
|
|
||||||
|
|
||||||
|
|
||||||
// If we made it this far, we were given a data type that we're not actively supporting.
|
|
||||||
// As such, we just have to hand this off to the native serializer.
|
|
||||||
writeOutput( serializeJson( input ) );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* I serialize and write the given string to the current output context, escaping all appropriate
|
|
||||||
* characters for the JSON specification.
|
|
||||||
*
|
|
||||||
* NOTE: We are using this manual-encoding process rather than the built-in serializeJson() function
|
|
||||||
* as there is a rather nasty bug that corrupts certain patterns in the output. Read more:
|
|
||||||
*
|
|
||||||
* http://www.bennadel.com/blog/2842-serializejson-and-the-input-and-output-encodings-are-not-same-errors-in-coldfusion.htm
|
|
||||||
*
|
|
||||||
* @input I am the string being serialized.
|
|
||||||
*/
|
|
||||||
private void function serializeInputString( required string input ) {
|
|
||||||
|
|
||||||
// While this may not be technically needed, this will ensure that we are not using any
|
|
||||||
// "undocumented features" of the language. If we explicitly cast to a Java string, and
|
|
||||||
// something goes wrong due to odd type-casting, it's a ColdFusion bug, at that point, not
|
|
||||||
// a logic error ;)
|
|
||||||
input = javaCast( "string", input );
|
|
||||||
|
|
||||||
var length = input.length();
|
|
||||||
|
|
||||||
writeOutput( """" );
|
|
||||||
|
|
||||||
for ( var i = 1 ; i <= length ; i++ ) {
|
|
||||||
|
|
||||||
var charCode = input.codePointAt( javaCast( "int", i - 1 ) );
|
|
||||||
|
|
||||||
// Check for the most common case first (normal characters).
|
|
||||||
if (
|
|
||||||
( charCode >= 32 ) &&
|
|
||||||
( charCode != 34 ) &&
|
|
||||||
( charCode != 47 ) &&
|
|
||||||
( charCode != 92 ) &&
|
|
||||||
( charCode != 8232 ) &&
|
|
||||||
( charCode != 8233 )
|
|
||||||
) {
|
|
||||||
|
|
||||||
writeOutput( chr( charCode ) );
|
|
||||||
|
|
||||||
// Check for the special cases next (control characters, characters that
|
|
||||||
// need to be escaped, and characters that need to be encoded nicely).
|
|
||||||
} else if ( charCode == 8 ) {
|
|
||||||
|
|
||||||
writeOutput( "\b" );
|
|
||||||
|
|
||||||
} else if ( charCode == 9 ) {
|
|
||||||
|
|
||||||
writeOutput( "\t" );
|
|
||||||
|
|
||||||
} else if ( charCode == 10 ) {
|
|
||||||
|
|
||||||
writeOutput( "\n" );
|
|
||||||
|
|
||||||
} else if ( charCode == 12 ) {
|
|
||||||
|
|
||||||
writeOutput( "\f" );
|
|
||||||
|
|
||||||
} else if ( charCode == 13 ) {
|
|
||||||
|
|
||||||
writeOutput( "\r" );
|
|
||||||
|
|
||||||
} else if (
|
|
||||||
( charCode < 32 ) ||
|
|
||||||
( charCode == 8232 ) ||
|
|
||||||
( charCode == 8233 )
|
|
||||||
) {
|
|
||||||
|
|
||||||
// For Unicode hex values, we need to enforce a 4-digit code.
|
|
||||||
writeOutput( "\u" & right( ( "000" & formatBaseN( charCode, 16 ) ), 4 ) );
|
|
||||||
|
|
||||||
} else if ( charCode == 34 ) {
|
|
||||||
|
|
||||||
writeOutput( "\""" );
|
|
||||||
|
|
||||||
} else if ( charCode == 47 ) {
|
|
||||||
|
|
||||||
writeOutput( "\/" );
|
|
||||||
|
|
||||||
} else if ( charCode == 92 ) {
|
|
||||||
|
|
||||||
writeOutput( "\\" );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
writeOutput( """" );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* I serialize and return the given string, escaping all appropriate characters for the
|
|
||||||
* JSON specification.
|
|
||||||
*
|
|
||||||
* @input I am the string being serialized.
|
|
||||||
* @output false
|
|
||||||
*/
|
|
||||||
private string function serializeString( required string input ) {
|
|
||||||
|
|
||||||
savecontent variable = "local.json" {
|
|
||||||
|
|
||||||
serializeInputString( input );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return( json );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
<cfcomponent extends="taffy.core.baseSerializer">
|
|
||||||
|
|
||||||
<cffunction
|
|
||||||
name="getAsJson"
|
|
||||||
output="false"
|
|
||||||
taffy:mime="application/json"
|
|
||||||
taffy:default="true">
|
|
||||||
<cfreturn variables.jsonUtil.serialize(variables.data) />
|
|
||||||
</cffunction>
|
|
||||||
|
|
||||||
<cffunction name="setJSONUtil" output="false">
|
|
||||||
<cfargument name="JSONUtil" required="true" />
|
|
||||||
<cfset variables.jsonUtil = arguments.JSONUtil />
|
|
||||||
</cffunction>
|
|
||||||
|
|
||||||
</cfcomponent>
|
|
||||||
Reference in New Issue
Block a user