/* * Copyright (C) 2000 - 2012 TagServlet Ltd * * This file is part of Open BlueDragon (OpenBD) CFML Server Engine. * * OpenBD is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * Free Software Foundation,version 3. * * OpenBD is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenBD. If not, see http://www.gnu.org/licenses/ * * Additional permission under GNU GPL version 3 section 7 * * If you modify this Program, or any covered work, by linking or combining * it with any of the JARS listed in the README.txt (or a modified version of * (that library), containing parts covered by the terms of that JAR, the * licensors of this Program grant you additional permission to convey the * resulting work. * README.txt @ http://www.openbluedragon.org/license/README.txt * * http://openbd.org/ * * $Id: serializejson.java 2387 2013-06-17 02:07:50Z alan $ */ package com.naryx.tagfusion.expression.function.string; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import java.util.TimeZone; import com.mongodb.QueryBuilder; import com.naryx.tagfusion.cfm.engine.cfArgStructData; import com.naryx.tagfusion.cfm.engine.cfArrayData; import com.naryx.tagfusion.cfm.engine.cfData; import com.naryx.tagfusion.cfm.engine.cfDateData; import com.naryx.tagfusion.cfm.engine.cfEngine; import com.naryx.tagfusion.cfm.engine.cfJavaObjectData; import com.naryx.tagfusion.cfm.engine.cfQueryResultData; import com.naryx.tagfusion.cfm.engine.cfSession; import com.naryx.tagfusion.cfm.engine.cfStringData; import com.naryx.tagfusion.cfm.engine.cfStructData; import com.naryx.tagfusion.cfm.engine.cfmRunTimeException; import com.naryx.tagfusion.cfm.engine.dataNotSupportedException; import com.naryx.tagfusion.expression.function.functionBase; /* * Takes in a cfData and converts it to a JSON string * * It adds a new parameter to the end, that allows you to force all column * names to lowercase, instead of the default uppercase. * */ public class serializejson extends functionBase { public enum CaseType { LOWER, UPPER, MAINTAIN }; public enum DateType { LONG, HTTP, JSON, CFML, MONGO } private static final long serialVersionUID = 1L; private static SimpleDateFormat dateLongFormat = new SimpleDateFormat("MMMM, d yyyy HH:mm:ss"); private static SimpleDateFormat dateHTTPFormat = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss"); private static SimpleDateFormat dateJSONFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); private static SimpleDateFormat dateMongoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); static { dateMongoFormat.setTimeZone( TimeZone.getTimeZone("GMT") ); } public serializejson() { min = 1; max = 4; setNamedParams( new String[]{ "object", "sercols", "conv", "date" } ); } public String[] getParamInfo(){ return new String[]{ "object", "serialize column names - default false", "key case - how to encode the keys. Values are: lower, upper or maintain. Default=" + cfEngine.DefaultJSONReturnCase + ". You can change the default encoding using the bluedragon.xml flag: server.system.jsoncase", "date formatting - how dates are encoded. Values are: LONG, HTTP, JSON, CFML, MONGO. Default=" + cfEngine.DefaultJSONReturnDate + ". You can change the default encoding using the bluedragon.xml flag: server.system.jsondate" }; } public java.util.Map getInfo(){ return makeInfo( "conversion", "Encodes the given object to a JSON string.", ReturnType.STRING ); } public static CaseType getCaseType(String tmp ){ if ( tmp == null ) return getCaseType( cfEngine.DefaultJSONReturnCase ); tmp = tmp.toLowerCase(); if ( tmp.equals("true") || tmp.equals("lower") ) return CaseType.LOWER; else if ( tmp.equals("false") || tmp.equals("upper") ) return CaseType.UPPER; else if ( tmp.equals("maintain") ) return CaseType.MAINTAIN; else return getCaseType( cfEngine.DefaultJSONReturnCase ); } /** * For the DateType string get the datetype * @param tmp * @return */ public static DateType getDateType(String tmp){ if ( tmp == null ) return getDateType( cfEngine.DefaultJSONReturnDate ); tmp = tmp.toLowerCase(); if ( tmp.equals("long") ) return DateType.LONG; else if ( tmp.equals("http") ) return DateType.HTTP; else if ( tmp.equals("json") ) return DateType.JSON; else if ( tmp.equals("cfml") ) return DateType.CFML; else if ( tmp.equals("mongo") ) return DateType.MONGO; else return getDateType( cfEngine.DefaultJSONReturnDate ); } public cfData execute(cfSession _session, cfArgStructData argStruct ) throws cfmRunTimeException { cfData var = getNamedParam(argStruct, "object"); boolean serializeQueryByColumns = getNamedBooleanParam(argStruct, "sercols", false); CaseType caseConversion = getCaseType( getNamedStringParam(argStruct, "conv", "" ) ); DateType datetype = getDateType( getNamedStringParam(argStruct, "date", "" ) ); /* Serialise the data */ StringBuilder buffer = new StringBuilder(5000); encodeJSON(buffer, var, serializeQueryByColumns, caseConversion, datetype); return new cfStringData(buffer.toString()); } public void encodeJSON(StringBuilder out, cfData var, boolean serializeQueryByColumns, CaseType caseConversion, DateType datetype) throws dataNotSupportedException { if ( var == null || var.getDataType() == cfData.CFNULLDATA) { out.append( "null" ); }else if ( var.getDataType() == cfData.CFSTRINGDATA ){ /* * Encode Strings */ out.append("\"" + encodeString(var.getString()) + "\""); } else if (var.getDataType() == cfData.CFBOOLEANDATA) { /* * Boolean Data */ out.append(var.getBoolean() ? "true" : "false"); } else if (var.getDataType() == cfData.CFDATEDATA) { /* * Encode Date */ Date localeDate = (Date)((cfDateData)var).getUnderlyingInstance(); if ( datetype == DateType.LONG ) out.append("\"" + dateLongFormat.format(localeDate) + "\""); else if ( datetype == DateType.HTTP ) out.append("\"" + dateHTTPFormat.format(localeDate) + "\""); else if ( datetype == DateType.JSON ) out.append("\"" + dateJSONFormat.format(localeDate) + "\""); else if ( datetype == DateType.MONGO ) out.append("{$date:\"" + dateMongoFormat.format(localeDate) + "\"}"); else out.append("\"" + var.getString() + "\""); } else if (cfData.isSimpleValue(var)) { /* * Encode numbers and booleans */ out.append(var.getString()); } else if (var.getDataType() == cfData.CFARRAYDATA) { /* * Encode Arrays */ out.append("["); cfArrayData cfarraydata = (cfArrayData) var; for (int x = 0; x < cfarraydata.size(); x++) { encodeJSON(out, cfarraydata.getElement(x + 1), serializeQueryByColumns, caseConversion, datetype); if (x < cfarraydata.size() - 1) out.append(","); } out.append("]"); } else if (var.getDataType() == cfData.CFSTRUCTDATA) { /* * Encode Structs */ out.append("{"); cfStructData cfstructdata = (cfStructData) var; Object[] keys = cfstructdata.keys(); String key, value = null; for (int x = 0; x < keys.length; x++) { key = (String) keys[x]; if ( caseConversion == CaseType.LOWER ) value = key.toLowerCase(); else if ( caseConversion == CaseType.UPPER ) value = key.toUpperCase(); else if ( caseConversion == CaseType.MAINTAIN ) value = key; out.append("\"" + encodeString(value) + "\""); out.append(":"); encodeJSON(out, cfstructdata.getData(key), serializeQueryByColumns, caseConversion, datetype); if (x < keys.length - 1) out.append(","); } out.append("}"); } else if (var.getDataType() == cfData.CFQUERYRESULTDATA) { /* * Encode Query */ out.append("{"); if (serializeQueryByColumns) encodeQueryByColumnsJSON(out, (cfQueryResultData) var, serializeQueryByColumns, caseConversion, datetype); else encodeQueryByRowsJSON(out, (cfQueryResultData) var, serializeQueryByColumns, caseConversion, datetype); out.append("}"); } else if (var.getDataType() == cfData.CFJAVAOBJECTDATA) { Object o = ((cfJavaObjectData)var).getUnderlyingInstance(); if ( o instanceof org.bson.types.ObjectId ) out.append("{$oid:\"" + ((org.bson.types.ObjectId)o).toString() + "\"}"); else if ( o instanceof java.util.regex.Pattern ){ QueryBuilder qb = new QueryBuilder(); qb.put("tmp"); qb.regex( (java.util.regex.Pattern)o ); String json = qb.get().toString(); json = json.substring( json.indexOf("{",1), json.lastIndexOf("}") ); out.append(json); } else out.append("\"" + encodeString( String.valueOf(o) ) + "\""); } else { out.append("\"unsupported\""); } } private void encodeQueryByRowsJSON(StringBuilder out, cfQueryResultData querydata, boolean serializeQueryByColumns, CaseType caseConversion, DateType datetype ) throws dataNotSupportedException { /* Output the columns */ if (caseConversion == CaseType.LOWER) out.append("\"columns\":["); else out.append("\"COLUMNS\":["); String[] columns = querydata.getColumnList(); String value = null; for (int x = 0; x < columns.length; x++) { if ( caseConversion == CaseType.LOWER ) value = columns[x].toLowerCase(); else if ( caseConversion == CaseType.UPPER ) value = columns[x].toUpperCase(); else if ( caseConversion == CaseType.MAINTAIN ) value = columns[x]; out.append("\"" + encodeString(value) + "\""); if (x < columns.length - 1) out.append(","); } out.append("],"); /* Output the rows */ if (caseConversion == CaseType.LOWER) out.append("\"data\":["); else out.append("\"DATA\":["); int totalRows = querydata.getSize(); for (int x = 0; x < totalRows; x++) { out.append("["); /* Loop around each of the columns */ List<cfData> row = querydata.getRow(x); for (int r = 0; r < row.size(); r++) { encodeJSON(out, row.get(r), serializeQueryByColumns, caseConversion, datetype); if (r < row.size() - 1) out.append(","); } out.append("]"); if (x < totalRows - 1) out.append(","); } out.append("]"); } private void encodeQueryByColumnsJSON(StringBuilder out, cfQueryResultData querydata, boolean serializeQueryByColumns, CaseType caseConversion, DateType datetype ) throws dataNotSupportedException { /* * The rowcount */ int totalRows = querydata.getSize(); if ( caseConversion == CaseType.LOWER ) out.append("\"rowcount\":"); else out.append("\"ROWCOUNT\":"); out.append(totalRows); /* * The columns */ if ( caseConversion == CaseType.LOWER ) out.append(",\"columns\":["); else out.append(",\"COLUMNS\":["); String[] columns = querydata.getColumnList(); String value = null; for (int x = 0; x < columns.length; x++) { if ( caseConversion == CaseType.LOWER ) value = columns[x].toLowerCase(); else if ( caseConversion == CaseType.UPPER ) value = columns[x].toUpperCase(); else if ( caseConversion == CaseType.MAINTAIN ) value = columns[x]; out.append("\"" + encodeString(value) + "\""); if (x < columns.length - 1) out.append(","); } out.append("],"); /* * The data */ if ( caseConversion == CaseType.LOWER ) out.append("\"data\":{"); else out.append("\"DATA\":{"); for (int c = 0; c < columns.length; c++) { if ( caseConversion == CaseType.LOWER ) value = columns[c].toLowerCase(); else if ( caseConversion == CaseType.UPPER ) value = columns[c].toUpperCase(); else if ( caseConversion == CaseType.MAINTAIN ) value = columns[c]; out.append("\"" + encodeString(value) + "\":["); for (int r = 0; r < totalRows; r++) { List<cfData> row = querydata.getRow(r); encodeJSON(out, row.get(c), serializeQueryByColumns, caseConversion, datetype); if (r < totalRows - 1) out.append(","); } out.append("]"); if (c < columns.length - 1) out.append(","); } out.append("}"); } private String encodeString(String str) { str = com.nary.util.string.replaceString(str, "\\", "\\\\"); str = com.nary.util.string.replaceString(str, "\r", "\\r"); str = com.nary.util.string.replaceString(str, "\n", "\\n"); str = com.nary.util.string.replaceString(str, "\t", "\\t"); return com.nary.util.string.replaceString(str, "\"", "\\\""); } }