cleanup: remove backup serializer artifacts

This commit is contained in:
“Naeel”
2026-04-29 21:54:22 +04:00
parent 22d12ab070
commit 0ee66e8bd3
5 changed files with 20 additions and 1174 deletions
+20
View File
@@ -577,6 +577,26 @@
- синтаксических ошибок в `v1/lib/rest_api_helper.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` на этом этапе не затрагивается, потому что для его файлов нужна отдельная проверка по назначению и ссылкам.
### Следующий мини-этап
Для следующего такого же безопасного прохода выбраны:
-620
View File
@@ -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>
-19
View File
@@ -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>
-519
View File
@@ -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 );
}
}
-16
View File
@@ -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>