package triaina.commons.utils; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import triaina.commons.exception.JSONRuntimeException; import android.net.Uri; import android.os.Bundle; public final class JSONObjectUtils { private JSONObjectUtils() {} public static JSONObject parse(String jsonText) throws JSONRuntimeException { if (jsonText == null) { throw new JSONRuntimeException("cannot parse null"); } try { return new JSONObject(jsonText); } catch (JSONException exp) { throw new JSONRuntimeException(exp); } } public static <T> void put(JSONObject json, String key, T value) { try { json.put(key, (T)value); } catch (JSONException exp) { throw new JSONRuntimeException(exp); } } public static Object get(JSONObject json, String key) { try { return json.get(key); } catch (JSONException exp) { throw new JSONRuntimeException(exp); } } public static String getString(JSONObject json, String key) { try { return json.getString(key); } catch (JSONException exp) { throw new JSONRuntimeException(exp); } } public static double getDouble(JSONObject json, String key) { try { return json.getDouble(key); } catch (JSONException exp) { throw new JSONRuntimeException(exp); } } public static int getInt(JSONObject json, String key) { try { return json.getInt(key); } catch (JSONException exp) { throw new JSONRuntimeException(exp); } } public static boolean getBoolean(JSONObject json, String key) { try { return json.getBoolean(key); } catch (JSONException exp) { throw new JSONRuntimeException(exp); } } public static JSONObject getJSONObject(JSONObject json, String key) { try { return json.getJSONObject(key); } catch (JSONException exp) { throw new JSONRuntimeException(exp); } } public static JSONArray getJSONArray(JSONObject json, String key) { try { return json.getJSONArray(key); } catch (JSONException exp) { throw new JSONRuntimeException(exp); } } public static Map<String, String> getMap(JSONObject json, String key) { JSONObject obj = JSONObjectUtils.getJSONObject(json, key); return JSONObjectUtils.toMap(obj); } public static Bundle getBundle(JSONObject json, String key) { JSONObject obj = JSONObjectUtils.getJSONObject(json, key); return JSONObjectUtils.toBundle(obj); } public static String toUriQueryParameter(JSONObject json) { StringBuilder builder = new StringBuilder(); builder.append('?'); for (@SuppressWarnings("unchecked") Iterator<String> itr = json.keys(); itr.hasNext();) { String key = itr.next(); builder.append(key); builder.append('='); builder.append(Uri.encode(json.optString(key))); if (itr.hasNext()) { builder.append('&'); } } return builder.toString(); } public static Map<String, String> toMap(JSONObject json) { Map<String, String> map = new HashMap<String, String>(); for (@SuppressWarnings("unchecked") Iterator<String> itr = json.keys(); itr.hasNext();) { String key = itr.next(); map.put(key, json.optString(key)); } return map; } public static Bundle toBundle(JSONObject json) { Bundle bundle = new Bundle(); for (@SuppressWarnings("unchecked") Iterator<String> itr = json.keys(); itr.hasNext();) { String key = itr.next(); bundle.putString(key, json.optString(key)); } return bundle; } /** * Compares 2 JSON objects. * It compares the actual JSON data, and considers a NULL or non-specified value for a key equivalent to an absence of key (i.e. "{key:null}" is considered equivalent to "{}"). * Not that values are not coerced for comparison, so "{key:10}" isn't equivalent to "{key:'10'}". * @param left The first JSON data to compare * @param right The JSON data to compare to {@link left} * @return true if the 2 JSON data are equivalent per the above description, false otherwise */ public static boolean areDataEquivalent (final JSONObject left, final JSONObject right) { if ((left == null) || (right == null)) { return (left == right); //whether both null or not } //special simple case if (left.toString().equals(right.toString())) return true; //else, perform a full comparison : final String[] keysToCompare = new String[left.length() + right.length()]; //will contain all keys to compare @SuppressWarnings("unchecked") final Iterator<String> leftKeysIt = left.keys(); //copy keys to a common array (O(n)) : int i = 0; for (; leftKeysIt.hasNext(); ++i) keysToCompare[i] = leftKeysIt.next(); @SuppressWarnings("unchecked") final Iterator<String> rightKeysIt = right.keys(); for (; rightKeysIt.hasNext(); ++i) keysToCompare[i] = rightKeysIt.next(); //sort keys (O(n logn)) : Arrays.sort(keysToCompare); //compare left and right for each key (O(n)) : for (i = 0; i < keysToCompare.length; ++i) { if ((i > 0) && (keysToCompare[i].equals(keysToCompare[i-1]))) //already checked this key continue; final String key = keysToCompare[i]; Object leftObj; try { leftObj = (left.has(key)) ? left.get(key) : JSONObject.NULL; } catch (final JSONException jE) { throw new JSONRuntimeException(jE); //shouldn't happen as we check for has() before calling get() } Object rightObj; try { rightObj = (right.has(key)) ? right.get(key) : JSONObject.NULL; } catch (final JSONException jE) { throw new JSONRuntimeException(jE); //shouldn't happen as we check for has() before calling get() } //compare sub-objects : if ((leftObj instanceof JSONObject) && (rightObj instanceof JSONObject)) { if (!(areDataEquivalent((JSONObject)leftObj, (JSONObject)rightObj))) return false; } else { if (!leftObj.equals(rightObj)) return false; //they're not equivalent } //Android's JSONArray overrides equals() so no need for special case for it } //didn't return false yet, objects are equal : return true; } }