/*
* Copyright (C) 2000 - 2013 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: deserializejson.java 2419 2013-11-25 21:27:42Z andy $
*/
package com.naryx.tagfusion.expression.function.string;
import java.util.Iterator;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
import com.naryx.tagfusion.cfm.engine.cfArgStructData;
import com.naryx.tagfusion.cfm.engine.cfArrayData;
import com.naryx.tagfusion.cfm.engine.cfBooleanData;
import com.naryx.tagfusion.cfm.engine.cfData;
import com.naryx.tagfusion.cfm.engine.cfNullData;
import com.naryx.tagfusion.cfm.engine.cfNumberData;
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 JSON String and converts it to a cfData
*
* April 2011: Please note that this implementation is currently not being utilised in
* favor of the DeserializeJSONJackson version that utilises the Jackson library
*
* Will be removed from subsequent versions
*
*/
public class deserializejson extends functionBase {
private static final long serialVersionUID = 1L;
public deserializejson() {
min = 1;
max = 2;
setNamedParams( new String[]{ "jsonstring", "strictmapping" } );
}
public String[] getParamInfo(){
return new String[]{
"JSON string",
"strict mapping flag"
};
}
public java.util.Map getInfo(){
return makeInfo(
"conversion",
"Decodes the given JSON string into a CFML object",
ReturnType.OBJECT );
}
public cfData execute(cfSession _session, cfArgStructData argStruct ) throws cfmRunTimeException {
boolean strictMapping = getNamedBooleanParam( argStruct, "strictmapping", true );
String jsonString = getNamedStringParam( argStruct, "jsonstring","");
try {
return getCfDataFromJSon( jsonString, strictMapping );
} catch (Exception e) {
throwException(_session, "invalid JSON string");
return null;
}
}
/**
* Utilised by cfEngine.serviceCFCMethod() when decoded JSON params coming from the CFAJAXProxy calls.
* We only do the params specified as we do not wish to be JSONDecoding stuff that would have been in the
* normal form
*
* @param args
* @throws Exception
* @throws cfmRunTimeException
* @throws dataNotSupportedException
*/
public static cfArgStructData transformStructElements(cfArgStructData args, String params[] ) throws dataNotSupportedException, cfmRunTimeException, Exception{
cfArgStructData decodedArgs = new cfArgStructData();
Iterator<String> it = args.keySet().iterator();
while ( it.hasNext() ){
String k = it.next();
if ( params != null && params.length > 0 ){
boolean processIt = false;
for ( int x=0; x < params.length; x++ ){
if ( params[x].equalsIgnoreCase(k) && !params[x].startsWith("__BD") ){
processIt = true;
break;
}
}
if (!processIt)
continue;
}
cfData arg = args.getData(k);
if (arg != null){
arg = getCfDataFromJSon(arg.getString(),true);
}
decodedArgs.setData(k, arg);
it.remove();
}
return decodedArgs;
}
public static cfData getCfDataFromJSon(String jsonString, boolean strictMapping) throws Exception {
if ( jsonString.isEmpty() )
return cfStringData.EMPTY_STRING;
else if (jsonString.startsWith("{"))
return convertToCf(new JSONObject(jsonString), strictMapping);
else if (jsonString.startsWith("["))
return convertToCf(new JSONArray(jsonString), strictMapping);
else{
JSONTokener tokener = new JSONTokener( jsonString );
Object value = tokener.nextValue();
if ( tokener.next() > 0 ){
throw new Exception("invalid JSON string");
}
if ( value instanceof String ){
return new cfStringData( (String) value );
}else if ( value instanceof Boolean ){
return cfBooleanData.getcfBooleanData( (Boolean) value, ( (Boolean) value ).booleanValue() ? "true" : "false" );
}else if ( value instanceof Number ){
return cfNumberData.getNumber( (Number) value );
}else if ( value == JSONObject.NULL ){
return cfNullData.NULL;
}else
return new cfStringData( jsonString );
}
}
private static cfData convertToCf(Object jsonData, boolean strictMapping) throws Exception {
if (jsonData instanceof JSONArray) {
JSONArray jsonArray = (JSONArray) jsonData;
cfArrayData cfarray = cfArrayData.createArray(1);
for (int x = 0; x < jsonArray.length(); x++) {
if (jsonArray.isNull(x)) {
cfarray.addElement(new cfNullData());
continue;
}
try {
cfarray.addElement(convertToCf(jsonArray.getJSONArray(x), strictMapping));
continue;
} catch (JSONException notThisType) {}
try {
cfarray.addElement(convertToCf(jsonArray.getJSONObject(x), strictMapping));
continue;
} catch (JSONException notThisType) {}
/* only convert to a number if the original value is not a string */
String str = jsonArray.getString(x);
if (!(jsonArray.get(x) instanceof String)) {
try {
cfNumberData num = new cfNumberData(jsonArray.getDouble(x));
// Check to see if this has leading numbers
if (str.length() > 1 && str.charAt(0) == '0' && str.indexOf(".") == -1) {
// has a leading zero
} else {
cfarray.addElement( num );
continue;
}
} catch (JSONException notThisType) {}
}
try {
cfarray.addElement(cfBooleanData.getcfBooleanData(jsonArray.getBoolean(x)));
continue;
} catch (JSONException notThisType) {}
cfarray.addElement( new cfStringData( str ) );
}
return cfarray;
} else if (jsonData instanceof JSONObject) {
JSONObject jsonObject = (JSONObject) jsonData;
if (strictMapping || (!strictMapping && !isQuery(jsonObject))) {
cfStructData cfstruct = new cfStructData();
Iterator<String> keysIt = jsonObject.keys();
while (keysIt.hasNext()) {
String key = keysIt.next();
if (jsonObject.isNull(key)) {
cfstruct.setData(key, new cfNullData());
continue;
}
try {
cfstruct.setData(key, convertToCf(jsonObject.getJSONArray(key), strictMapping));
continue;
} catch (JSONException notThisType) {}
try {
cfstruct.setData(key, convertToCf(jsonObject.getJSONObject(key), strictMapping));
continue;
} catch (JSONException notThisType) {}
/* only convert to a number if the original value is not a string */
String str = jsonObject.getString(key);
if (!(jsonObject.get(key) instanceof String)) {
try {
cfNumberData num = new cfNumberData(jsonObject.getDouble(key));
// Check to see if this has leading numbers
if (str.length() > 1 && str.charAt(0) == '0' && str.indexOf(".") == -1) {
// has a leading zero
} else {
cfstruct.setData(key, num);
continue;
}
} catch (JSONException notThisType) {}
}
try {
cfstruct.setData(key, cfBooleanData.getcfBooleanData(jsonObject.getBoolean(key)));
continue;
} catch (JSONException notThisType) {}
cfstruct.setData(key, new cfStringData(str));
}
return cfstruct;
} else
return new cfJSONQueryData(jsonObject);
}
return null;
}
private static boolean isQuery(JSONObject jsonObject) {
/*
* Determines to see if this is a CFQUERY object
*/
/* Try the first method */
try {
jsonObject.getJSONArray("COLUMNS");
jsonObject.getJSONArray("DATA");
return true;
} catch (Exception e) {}
try {
jsonObject.getJSONArray("columns");
jsonObject.getJSONArray("data");
return true;
} catch (Exception e) {}
try {
jsonObject.getInt("ROWCOUNT");
jsonObject.getJSONArray("COLUMNS");
jsonObject.getJSONObject("DATA");
return true;
} catch (Exception e) {}
try {
jsonObject.getInt("rowcount");
jsonObject.getJSONArray("columns");
jsonObject.getJSONObject("data");
return true;
} catch (Exception e) {}
return false;
}
}