/* * Licensed to the Apache Software Foundation (ASF) under one or more contributor license * agreements. See the NOTICE file distributed with this work for additional information regarding * copyright ownership. The ASF licenses this file to You 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. */ package org.apache.geode.management.internal.cli.util; import java.lang.reflect.Array; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import org.apache.geode.internal.ClassPathLoader; import org.apache.geode.management.internal.cli.json.GfJsonArray; import org.apache.geode.management.internal.cli.json.GfJsonException; import org.apache.geode.management.internal.cli.json.GfJsonObject; import org.apache.geode.management.internal.cli.result.CliJsonSerializable; import org.apache.geode.management.internal.cli.result.CliJsonSerializableFactory; import org.apache.geode.management.internal.cli.result.ResultDataException; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; /** * This class contains utility methods for JSON (http://www.json.org/) which is used by classes used * for the Command Line Interface (CLI). * * * @since GemFire 7.0 */ public class JsonUtil { /** * Converts given JSON String in to a Map. Refer http://www.json.org/ to construct a JSON format. * * @param jsonString jsonString to be converted in to a Map. * @return a Map created from * * @throws IllegalArgumentException if the specified JSON string can not be converted in to a Map */ public static Map<String, String> jsonToMap(String jsonString) { Map<String, String> jsonMap = new TreeMap<String, String>(); try { GfJsonObject jsonObject = new GfJsonObject(jsonString); Iterator<String> keys = jsonObject.keys(); while (keys.hasNext()) { String key = keys.next(); jsonMap.put(key, jsonObject.getString(key)); } } catch (GfJsonException e) { throw new IllegalArgumentException( "Could not convert jsonString : '" + jsonString + "' to map."); } return jsonMap; } /** * Converts given Map in to a JSON string representing a Map. Refer http://www.json.org/ for more. * * @param properties a Map of Strings to be converted in to JSON String * @return a JSON string representing the specified Map. */ public static String mapToJson(Map<String, String> properties) { return new GfJsonObject(properties).toString(); } /** * Converts given Object in to a JSON string representing an Object. Refer http://www.json.org/ * for more. * * @param object an Object to be converted in to JSON String * @return a JSON string representing the specified object. */ public static String objectToJson(Object object) { return new GfJsonObject(object).toString(); } /** * Converts given Object in to a JSON string representing an Object. If object contains an * attribute which itself is another object it will be displayed as className if its json * representation exceeds the length * * @param object an Object to be converted in to JSON String * @return a JSON string representing the specified object. */ public static String objectToJsonNested(Object object, int length) { return objectToJsonNestedChkCDep(object, length, false); } public static String objectToJsonNestedChkCDep(Object object, int length) { return objectToJsonNestedChkCDep(object, length, true); } private static String objectToJsonNestedChkCDep(Object object, int length, boolean checkCyclicDep) { GfJsonObject jsonObject = new GfJsonObject(object, checkCyclicDep); Iterator<String> iterator = jsonObject.keys(); while (iterator.hasNext()) { String key = iterator.next(); Object value = jsonObject.get(key); if (value != null && !isPrimitiveOrWrapper(value.getClass())) { GfJsonObject jsonified = new GfJsonObject(value); String stringified = jsonified.toString(); try { if (stringified.length() > length) { jsonObject.put(key, jsonified.getType()); } else { jsonObject.put(key, stringified); } } catch (GfJsonException e) { e.printStackTrace(); } } } return jsonObject.toString(); } /** * Converts given JSON String in to a Object. Refer http://www.json.org/ to construct a JSON * format. * * @param jsonString jsonString to be converted in to a Map. * @return an object constructed from given JSON String * * @throws IllegalArgumentException if the specified JSON string can not be converted in to an * Object */ public static <T> T jsonToObject(String jsonString, Class<T> klass) { T objectFromJson = null; try { GfJsonObject jsonObject = new GfJsonObject(jsonString); objectFromJson = klass.newInstance(); Method[] declaredMethods = klass.getDeclaredMethods(); Map<String, Method> methodsMap = new HashMap<String, Method>(); for (Method method : declaredMethods) { methodsMap.put(method.getName(), method); } int noOfFields = jsonObject.size(); Iterator<String> keys = jsonObject.keys(); while (keys.hasNext()) { String key = keys.next(); Method method = methodsMap.get("set" + capitalize(key)); if (method != null) { Class<?>[] parameterTypes = method.getParameterTypes(); if (parameterTypes.length == 1) { Class<?> parameterType = parameterTypes[0]; Object value = jsonObject.get(key); if (isPrimitiveOrWrapper(parameterType)) { value = getPrimitiveOrWrapperValue(parameterType, value); } // Bug #51175 else if (isArray(parameterType)) { value = toArray(value, parameterType); } else if (isList(parameterType)) { value = toList(value, parameterType); } else if (isMap(parameterType)) { value = toMap(value, parameterType); } else if (isSet(parameterType)) { value = toSet(value, parameterType); } else { value = jsonToObject(value.toString(), parameterType); } method.invoke(objectFromJson, new Object[] {value}); noOfFields--; } } } if (noOfFields != 0) { throw new IllegalArgumentException( "Not enough setter methods for fields in given JSON String : " + jsonString + " in class : " + klass); } } catch (InstantiationException e) { throw new IllegalArgumentException("Couldn't convert JSON to Object of type " + klass, e); } catch (IllegalAccessException e) { throw new IllegalArgumentException("Couldn't convert JSON to Object of type " + klass, e); } catch (GfJsonException e) { throw new IllegalArgumentException("Couldn't convert JSON to Object of type " + klass, e); } catch (IllegalArgumentException e) { throw new IllegalArgumentException("Couldn't convert JSON to Object of type " + klass, e); } catch (InvocationTargetException e) { throw new IllegalArgumentException("Couldn't convert JSON to Object of type " + klass, e); } return objectFromJson; } private static Object toArray(Object value, Class<?> parameterType) throws GfJsonException { Class arrayComponentType = parameterType.getComponentType(); if (isPrimitiveOrWrapper(arrayComponentType)) { if (value instanceof JSONArray) { try { JSONArray jsonArray = (JSONArray) value; Object jArray = Array.newInstance(arrayComponentType, jsonArray.length()); for (int i = 0; i < jsonArray.length(); i++) { Array.set(jArray, i, jsonArray.get(i)); } return jArray; } catch (ArrayIndexOutOfBoundsException e) { throw new GfJsonException(e); } catch (IllegalArgumentException e) { throw new GfJsonException(e); } catch (JSONException e) { throw new GfJsonException(e); } } else { throw new GfJsonException("Expected JSONArray for array type"); } } else throw new GfJsonException( "Array contains non-primitive element. Non-primitive elements are not supported in json array"); } /** * This is used in Put command this method uses HashSet as default implementation * * @param value * @param parameterType * @return setValue * @throws GfJsonException */ @SuppressWarnings({"rawtypes", "unchecked"}) private static Object toSet(Object value, Class<?> parameterType) throws GfJsonException { try { JSONArray array = (JSONArray) value; Set set = new HashSet(); for (int i = 0; i < array.length(); i++) { Object element = array.get(i); if (isPrimitiveOrWrapper(element.getClass())) { set.add(element); } else throw new GfJsonException( "Only primitive types are supported in set type for input commands"); } return set; } catch (JSONException e) { throw new GfJsonException(e); } } private static Object toMap(Object value, Class<?> parameterType) throws GfJsonException { try { if (value instanceof JSONObject) { JSONObject obj = (JSONObject) value; Iterator iterator = obj.keys(); Map map = new HashMap(); while (iterator.hasNext()) { String key = (String) iterator.next(); Object elem; elem = obj.get(key); if (isPrimitiveOrWrapper(elem.getClass())) { map.put(key, elem); } else throw new GfJsonException( "Only primitive types are supported in map type for input commands"); } return map; } else throw new GfJsonException( "Expected JSONObject for Map. Retrieved type is " + value.getClass()); } catch (JSONException e) { throw new GfJsonException(e); } } private static Object toList(Object value, Class<?> parameterType) throws GfJsonException { try { JSONArray array = (JSONArray) value; List list = new ArrayList(); for (int i = 0; i < array.length(); i++) { Object element = array.get(i); if (isPrimitiveOrWrapper(element.getClass())) { list.add(element); } else throw new GfJsonException( "Only primitive types are supported in set type for input commands"); } return list; } catch (JSONException e) { throw new GfJsonException(e); } } public static Object jsonToObject(String jsonString) { Object objectFromJson = null; try { GfJsonObject jsonObject = new GfJsonObject(jsonString); Iterator<String> keys = jsonObject.keys(); Object[] arr = new Object[jsonObject.size()]; int i = 0; while (keys.hasNext()) { String key = keys.next(); Class<?> klass = ClassPathLoader.getLatest().forName(key); arr[i++] = jsonToObject((String) jsonObject.get(key).toString(), klass); } if (arr.length == 1) { objectFromJson = arr[0]; } else { objectFromJson = arr; } } catch (GfJsonException e) { throw new IllegalArgumentException("Couldn't convert JSON to Object.", e); } catch (ClassNotFoundException e) { throw new IllegalArgumentException("Couldn't convert JSON to Object.", e); } return objectFromJson; } public static String capitalize(String str) { String capitalized = str; if (str == null || str.isEmpty()) { return capitalized; } capitalized = String.valueOf(str.charAt(0)).toUpperCase() + str.substring(1); return capitalized; } private static boolean isArray(Class<?> parameterType) { return parameterType.isArray(); } public static boolean isList(Class<?> klass) { return klass.isAssignableFrom(List.class); } public static boolean isSet(Class<?> klass) { return klass.isAssignableFrom(Set.class); } public static boolean isMap(Class<?> klass) { return klass.isAssignableFrom(Map.class); } public static boolean isPrimitiveOrWrapper(Class<?> klass) { return klass.isAssignableFrom(Byte.class) || klass.isAssignableFrom(byte.class) || klass.isAssignableFrom(Short.class) || klass.isAssignableFrom(short.class) || klass.isAssignableFrom(Integer.class) || klass.isAssignableFrom(int.class) || klass.isAssignableFrom(Long.class) || klass.isAssignableFrom(long.class) || klass.isAssignableFrom(Float.class) || klass.isAssignableFrom(float.class) || klass.isAssignableFrom(Double.class) || klass.isAssignableFrom(double.class) || klass.isAssignableFrom(Boolean.class) || klass.isAssignableFrom(boolean.class) || klass.isAssignableFrom(String.class) || klass.isAssignableFrom(Character.class) || klass.isAssignableFrom(char.class); } public static Object getPrimitiveOrWrapperValue(Class<?> klass, Object value) throws IllegalArgumentException { if (klass.isAssignableFrom(Byte.class) || klass.isAssignableFrom(byte.class)) { return value; } else if (klass.isAssignableFrom(Short.class) || klass.isAssignableFrom(short.class)) { return value; } else if (klass.isAssignableFrom(Integer.class) || klass.isAssignableFrom(int.class)) { return value; } else if (klass.isAssignableFrom(Long.class) || klass.isAssignableFrom(long.class)) { return value; } else if (klass.isAssignableFrom(Float.class) || klass.isAssignableFrom(Float.class)) { return value; } else if (klass.isAssignableFrom(Double.class) || klass.isAssignableFrom(double.class)) { return value; } else if (klass.isAssignableFrom(Boolean.class) || klass.isAssignableFrom(boolean.class)) { return value; } else if (klass.isAssignableFrom(String.class)) { return String.valueOf(value); } else if (klass.isAssignableFrom(Character.class)) { // Need to take care of converting between string to char values if (value instanceof String) { String str = (String) value; if (str.length() == 1) return new Character(str.charAt(0)); else if (str.length() > 1 || str.length() == 0) { throw new IllegalArgumentException( "Expected Character value but found String with length " + str.length()); } } else if (value instanceof Character) { return value; } else { throw new IllegalArgumentException( "Expected Character value but found " + value.getClass()); } } else if (klass.isAssignableFrom(char.class)) { // Need to take care of converting between string to char values if (value instanceof String) { String str = (String) value; if (str.length() == 1) return str.charAt(0); else if (str.length() > 1 || str.length() == 0) { throw new IllegalArgumentException( "Expected Character value but found String with length " + str.length()); } } else if (value instanceof Character) { return ((Character) value).charValue(); } else { throw new IllegalArgumentException( "Expected Character value but found " + value.getClass()); } } else { return null; } return value; } public static int getInt(GfJsonObject jsonObject, String byName) { return jsonObject.getInt(byName); } public static long getLong(GfJsonObject jsonObject, String byName) { return jsonObject.getLong(byName); } public static double getDouble(GfJsonObject jsonObject, String byName) { return jsonObject.getDouble(byName); } public static boolean getBoolean(GfJsonObject jsonObject, String byName) { return jsonObject.getBoolean(byName); } public static String getString(GfJsonObject jsonObject, String byName) { return jsonObject.getString(byName); } public static GfJsonObject getJSONObject(GfJsonObject jsonObject, String byName) { return jsonObject.getJSONObject(byName); } public static String[] getStringArray(GfJsonObject jsonObject, String byName) { String[] stringArray = null; try { GfJsonArray jsonArray = jsonObject.getJSONArray(byName); stringArray = GfJsonArray.toStringArray(jsonArray); } catch (GfJsonException e) { throw new ResultDataException(e.getMessage()); } return stringArray; } public static byte[] getByteArray(GfJsonObject jsonObject, String byName) { byte[] byteArray = null; try { GfJsonArray jsonArray = jsonObject.getJSONArray(byName); byteArray = GfJsonArray.toByteArray(jsonArray); } catch (GfJsonException e) { throw new ResultDataException(e.getMessage()); } return byteArray; } public static List<CliJsonSerializable> getList(GfJsonObject jsonObject, String byName) { List<CliJsonSerializable> cliJsonSerializables = Collections.emptyList(); try { GfJsonArray cliJsonSerializableArray = jsonObject.getJSONArray(byName); int size = cliJsonSerializableArray.size(); if (size > 0) { cliJsonSerializables = new ArrayList<CliJsonSerializable>(); } for (int i = 0; i < size; i++) { GfJsonObject cliJsonSerializableState = cliJsonSerializableArray.getJSONObject(i); int jsId = cliJsonSerializableState.getInt(CliJsonSerializable.JSID); CliJsonSerializable cliJsonSerializable = CliJsonSerializableFactory.getCliJsonSerializable(jsId); cliJsonSerializable.fromJson(cliJsonSerializableState); cliJsonSerializables.add(cliJsonSerializable); } } catch (GfJsonException e) { throw new ResultDataException(e.getMessage()); } return cliJsonSerializables; } public static Set<CliJsonSerializable> getSet(GfJsonObject jsonObject, String byName) { Set<CliJsonSerializable> cliJsonSerializables = Collections.emptySet(); try { GfJsonArray cliJsonSerializableArray = jsonObject.getJSONArray(byName); int size = cliJsonSerializableArray.size(); if (size > 0) { cliJsonSerializables = new HashSet<CliJsonSerializable>(); } for (int i = 0; i < size; i++) { GfJsonObject cliJsonSerializableState = cliJsonSerializableArray.getJSONObject(i); int jsId = cliJsonSerializableState.getInt(CliJsonSerializable.JSID); CliJsonSerializable cliJsonSerializable = CliJsonSerializableFactory.getCliJsonSerializable(jsId); cliJsonSerializable.fromJson(cliJsonSerializableState); cliJsonSerializables.add(cliJsonSerializable); } } catch (GfJsonException e) { throw new ResultDataException(e.getMessage()); } return cliJsonSerializables; } // For testing purpose public static void main(String[] args) { System.out.println(capitalize("key")); System.out.println(capitalize("Key")); String str = "{\"org.apache.geode.management.internal.cli.JsonUtil$Employee\":{\"id\":1234,\"name\":\"Foo BAR\",\"department\":{\"id\":456,\"name\":\"support\"}}}"; Object jsonToObject = jsonToObject(str); System.out.println(jsonToObject); str = "{\"id\":1234,\"name\":\"Foo BAR\",\"department\":{\"id\":456,\"name\":\"support\"}}"; Object jsonToObject2 = jsonToObject(str, Employee.class); System.out.println(jsonToObject2); } public static class Employee { private int id; private String name; private Department department; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Department getDepartment() { return department; } public void setDepartment(Department department) { this.department = department; } @Override public String toString() { return "Employee [id=" + id + ", name=" + name + ", department=" + department + "]"; } } public static class Department { private int id; private String name; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Department [id=" + id + ", name=" + name + "]"; } } }