/* * 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. */ package org.skyscreamer.jsonassert.comparator; import java.util.ArrayList; import java.util.Collection; 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.TreeSet; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; /** * Utility class that contains Json manipulation methods. */ public final class JSONCompareUtil { private static Integer INTEGER_ONE = new Integer(1); private JSONCompareUtil() { } /** * Converts the provided {@link JSONArray} to a Map of {@link JSONObject}s where the key of each object * is the value at {@code uniqueKey} in each object. * * @param array the JSON array to convert * @param uniqueKey the key to map the JSON objects to * @return the map of {@link JSONObject}s from {@code array} * @throws JSONException JSON parsing error */ public static Map<Object, JSONObject> arrayOfJsonObjectToMap(JSONArray array, String uniqueKey) throws JSONException { Map<Object, JSONObject> valueMap = new HashMap<Object, JSONObject>(); for (int i = 0; i < array.length(); ++i) { JSONObject jsonObject = (JSONObject) array.get(i); Object id = jsonObject.get(uniqueKey); valueMap.put(id, jsonObject); } return valueMap; } /** * Searches for the unique key of the {@code expected} JSON array. * * @param expected the array to find the unique key of * @return the unique key if there's any, otherwise null * @throws JSONException JSON parsing error */ public static String findUniqueKey(JSONArray expected) throws JSONException { // Find a unique key for the object (id, name, whatever) JSONObject o = (JSONObject) expected.get(0); // There's at least one at this point for (String candidate : getKeys(o)) { if (isUsableAsUniqueKey(candidate, expected)) return candidate; } // No usable unique key :-( return null; } /** * <p>Looks to see if candidate field is a possible unique key across a array of objects. * Returns true IFF:</p> * <ol> * <li>array is an array of JSONObject * <li>candidate is a top-level field in each of of the objects in the array * <li>candidate is a simple value (not JSONObject or JSONArray) * <li>candidate is unique across all elements in the array * </ol> * * @param candidate is usable as a unique key if every element in the * @param array is a JSONObject having that key, and no two values are the same. * @return true if the candidate can work as a unique id across array * @throws JSONException JSON parsing error */ public static boolean isUsableAsUniqueKey(String candidate, JSONArray array) throws JSONException { Set<Object> seenValues = new HashSet<Object>(); for (int i = 0; i < array.length(); i++) { Object item = array.get(i); if (item instanceof JSONObject) { JSONObject o = (JSONObject) item; if (o.has(candidate)) { Object value = o.get(candidate); if (isSimpleValue(value) && !seenValues.contains(value)) { seenValues.add(value); } else { return false; } } else { return false; } } else { return false; } } return true; } /** * Converts the given {@link JSONArray} to a list of {@link Object}s. * * @param expected the JSON array to convert * @return the list of objects from the {@code expected} array * @throws JSONException JSON parsing error */ public static List<Object> jsonArrayToList(JSONArray expected) throws JSONException { List<Object> jsonObjects = new ArrayList<Object>(expected.length()); for (int i = 0; i < expected.length(); ++i) { jsonObjects.add(expected.get(i)); } return jsonObjects; } /** * Returns whether all of the elements in the given array are simple values. * * @param array the JSON array to iterate through on * @return true if all the elements in {@code array} are simple values * @throws JSONException JSON parsing error * @see #isSimpleValue(Object) */ public static boolean allSimpleValues(JSONArray array) throws JSONException { for (int i = 0; i < array.length(); ++i) { if (!isSimpleValue(array.get(i))) { return false; } } return true; } /** * Returns whether the given object is a simple value: not {@link JSONObject} and not {@link JSONArray}. * * @param o the object to inspect * @return true if {@code o} is a simple value */ public static boolean isSimpleValue(Object o) { return !(o instanceof JSONObject) && !(o instanceof JSONArray); } /** * Returns whether all elements in {@code array} are {@link JSONObject} instances. * * @param array the array to inspect * @return true if all the elements in the given array are JSONObjects * @throws JSONException JSON parsing error */ public static boolean allJSONObjects(JSONArray array) throws JSONException { for (int i = 0; i < array.length(); ++i) { if (!(array.get(i) instanceof JSONObject)) { return false; } } return true; } /** * Returns whether all elements in {@code array} are {@link JSONArray} instances. * * @param array the array to inspect * @return true if all the elements in the given array are JSONArrays * @throws JSONException JSON parsing error */ public static boolean allJSONArrays(JSONArray array) throws JSONException { for (int i = 0; i < array.length(); ++i) { if (!(array.get(i) instanceof JSONArray)) { return false; } } return true; } /** * Collects all keys in {@code jsonObject}. * * @param jsonObject the {@link JSONObject} to get the keys of * @return the set of keys */ public static Set<String> getKeys(JSONObject jsonObject) { Set<String> keys = new TreeSet<String>(); Iterator<?> iter = jsonObject.keys(); while (iter.hasNext()) { keys.add((String) iter.next()); } return keys; } public static String qualify(String prefix, String key) { return "".equals(prefix) ? key : prefix + "." + key; } public static String formatUniqueKey(String key, String uniqueKey, Object value) { return key + "[" + uniqueKey + "=" + value + "]"; } /** * Creates a cardinality map from {@code coll}. * * @param coll the collection of items to convert * @param <T> the type of elements in the input collection * @return the cardinality map */ public static <T> Map<T, Integer> getCardinalityMap(final Collection<T> coll) { Map count = new HashMap<T, Integer>(); for (T item : coll) { Integer c = (Integer) (count.get(item)); if (c == null) { count.put(item, INTEGER_ONE); } else { count.put(item, new Integer(c.intValue() + 1)); } } return count; } }