001package com.nimbusds.oauth2.sdk.util; 002 003 004import java.net.MalformedURLException; 005import java.net.URL; 006import java.util.HashSet; 007import java.util.List; 008import java.util.Set; 009 010import javax.mail.internet.AddressException; 011import javax.mail.internet.InternetAddress; 012 013import net.minidev.json.JSONArray; 014import net.minidev.json.JSONObject; 015import net.minidev.json.parser.JSONParser; 016 017import com.nimbusds.oauth2.sdk.ParseException; 018 019 020/** 021 * JSON object helper methods for parsing and typed retrieval of member values. 022 */ 023public class JSONObjectUtils { 024 025 026 /** 027 * Returns {@code true} if the JSON object is defined and contains the 028 * specified key. 029 * 030 * @param jsonObject The JSON object to check. May be {@code null}. 031 * @param key The key to check. Must not be {@code null}. 032 * 033 * @return {@code true} if the JSON object is defined and contains the 034 * specified key, else {@code false}. 035 */ 036 public static boolean containsKey(final JSONObject jsonObject, final String key) { 037 038 return jsonObject != null && jsonObject.containsKey(key); 039 } 040 041 042 /** 043 * Parses a JSON object. 044 * 045 * <p>Specific JSON to Java entity mapping (as per JSON Simple): 046 * 047 * <ul> 048 * <li>JSON numbers mapped to {@code java.lang.Number}. 049 * <li>JSON integer numbers mapped to {@code long}. 050 * <li>JSON fraction numbers mapped to {@code double}. 051 * </ul> 052 * 053 * @param s The JSON object string to parse. Must not be {@code null}. 054 * 055 * @return The JSON object. 056 * 057 * @throws ParseException If the string cannot be parsed to a JSON 058 * object. 059 */ 060 public static JSONObject parseJSONObject(final String s) 061 throws ParseException { 062 063 Object o; 064 065 try { 066 o = new JSONParser(JSONParser.USE_HI_PRECISION_FLOAT).parse(s); 067 068 } catch (net.minidev.json.parser.ParseException e) { 069 070 throw new ParseException("Invalid JSON: " + e.getMessage(), e); 071 } 072 073 if (o instanceof JSONObject) 074 return (JSONObject)o; 075 else 076 throw new ParseException("JSON entity is not an object"); 077 } 078 079 080 /** 081 * Gets a generic member of a JSON object. 082 * 083 * @param o The JSON object. Must not be {@code null}. 084 * @param key The JSON object member key. Must not be {@code null}. 085 * @param clazz The expected class of the JSON object member value. Must 086 * not be {@code null}. 087 * 088 * @return The JSON object member value. 089 * 090 * @throws ParseException If the value is missing, {@code null} or not 091 * of the expected type. 092 */ 093 @SuppressWarnings("unchecked") 094 private static <T> T getGeneric(final JSONObject o, final String key, final Class<T> clazz) 095 throws ParseException { 096 097 if (! o.containsKey(key)) 098 throw new ParseException("Missing JSON object member with key \"" + key + "\""); 099 100 if (o.get(key) == null) 101 throw new ParseException("JSON object member with key \"" + key + "\" has null value"); 102 103 Object value = o.get(key); 104 105 if (! clazz.isAssignableFrom(value.getClass())) 106 throw new ParseException("Unexpected type of JSON object member with key \"" + key + "\""); 107 108 return (T)value; 109 } 110 111 112 /** 113 * Gets a boolean member of a JSON object. 114 * 115 * @param o The JSON object. Must not be {@code null}. 116 * @param key The JSON object member key. Must not be {@code null}. 117 * 118 * @return The member value. 119 * 120 * @throws ParseException If the value is missing, {@code null} or not 121 * of the expected type. 122 */ 123 public static boolean getBoolean(final JSONObject o, final String key) 124 throws ParseException { 125 126 return getGeneric(o, key, Boolean.class); 127 } 128 129 130 /** 131 * Gets an number member of a JSON object as {@code int}. 132 * 133 * @param o The JSON object. Must not be {@code null}. 134 * @param key The JSON object member key. Must not be {@code null}. 135 * 136 * @return The member value. 137 * 138 * @throws ParseException If the value is missing, {@code null} or not 139 * of the expected type. 140 */ 141 public static int getInt(final JSONObject o, final String key) 142 throws ParseException { 143 144 return getGeneric(o, key, Number.class).intValue(); 145 } 146 147 148 /** 149 * Gets a number member of a JSON object as {@code long}. 150 * 151 * @param o The JSON object. Must not be {@code null}. 152 * @param key The JSON object member key. Must not be {@code null}. 153 * 154 * @return The member value. 155 * 156 * @throws ParseException If the value is missing, {@code null} or not 157 * of the expected type. 158 */ 159 public static long getLong(final JSONObject o, final String key) 160 throws ParseException { 161 162 return getGeneric(o, key, Number.class).longValue(); 163 } 164 165 166 /** 167 * Gets a number member of a JSON object {@code float}. 168 * 169 * @param o The JSON object. Must not be {@code null}. 170 * @param key The JSON object member key. Must not be {@code null}. 171 * 172 * @return The member value. 173 * 174 * @throws ParseException If the value is missing, {@code null} or not 175 * of the expected type. 176 */ 177 public static float getFloat(final JSONObject o, final String key) 178 throws ParseException { 179 180 return getGeneric(o, key, Number.class).floatValue(); 181 } 182 183 184 /** 185 * Gets a number member of a JSON object as {@code double}. 186 * 187 * @param o The JSON object. Must not be {@code null}. 188 * @param key The JSON object member key. Must not be {@code null}. 189 * 190 * @return The member value. 191 * 192 * @throws ParseException If the value is missing, {@code null} or not 193 * of the expected type. 194 */ 195 public static double getDouble(final JSONObject o, final String key) 196 throws ParseException { 197 198 return getGeneric(o, key, Number.class).doubleValue(); 199 } 200 201 202 /** 203 * Gets a string member of a JSON object. 204 * 205 * @param o The JSON object. Must not be {@code null}. 206 * @param key The JSON object member key. Must not be {@code null}. 207 * 208 * @return The member value. 209 * 210 * @throws ParseException If the value is missing, {@code null} or not 211 * of the expected type. 212 */ 213 public static String getString(final JSONObject o, final String key) 214 throws ParseException { 215 216 return getGeneric(o, key, String.class); 217 } 218 219 220 /** 221 * Gets a string member of a JSON object as an enumerated object. 222 * 223 * @param o The JSON object. Must not be {@code null}. 224 * @param key The JSON object member key. Must not be 225 * {@code null}. 226 * @param enumClass The enumeration class. Must not be {@code null}. 227 * 228 * @return The member value. 229 * 230 * @throws ParseException If the value is missing, {@code null} or not 231 * of the expected type. 232 */ 233 public static <T extends Enum<T>> T getEnum(final JSONObject o, 234 final String key, 235 final Class<T> enumClass) 236 throws ParseException { 237 238 String value = getString(o, key); 239 240 for (T en: enumClass.getEnumConstants()) { 241 242 if (en.toString().equalsIgnoreCase(value)) 243 return en; 244 } 245 246 throw new ParseException("Unexpected value of JSON object member with key \"" + key + "\""); 247 } 248 249 250 /** 251 * Gets a string member of a JSON object as {@code java.net.URL}. 252 * 253 * @param o The JSON object. Must not be {@code null}. 254 * @param key The JSON object member key. Must not be {@code null}. 255 * 256 * @return The member value. 257 * 258 * @throws ParseException If the value is missing, {@code null} or not 259 * of the expected type. 260 */ 261 public static URL getURL(final JSONObject o, final String key) 262 throws ParseException { 263 264 try { 265 return new URL(getGeneric(o, key, String.class)); 266 267 } catch (MalformedURLException e) { 268 269 throw new ParseException(e.getMessage(), e); 270 } 271 } 272 273 274 /** 275 * Gets a string member of a JSON object as 276 * {@code javax.mail.internet.InternetAddress}. 277 * 278 * @param o The JSON object. Must not be {@code null}. 279 * @param key The JSON object member key. Must not be {@code null}. 280 * 281 * @return The member value. 282 * 283 * @throws ParseException If the value is missing, {@code null} or not 284 * of the expected type. 285 */ 286 public static InternetAddress getEmail(final JSONObject o, final String key) 287 throws ParseException { 288 289 try { 290 final boolean strict = true; 291 292 return new InternetAddress(getGeneric(o, key, String.class), strict); 293 294 } catch (AddressException e) { 295 296 throw new ParseException(e.getMessage(), e); 297 } 298 } 299 300 301 /** 302 * Gets a JSON array member of a JSON object. 303 * 304 * @param o The JSON object. Must not be {@code null}. 305 * @param key The JSON object member key. Must not be {@code null}. 306 * 307 * @return The member value. 308 * 309 * @throws ParseException If the value is missing, {@code null} or not 310 * of the expected type. 311 */ 312 public static JSONArray getJSONArray(final JSONObject o, final String key) 313 throws ParseException { 314 315 return getGeneric(o, key, JSONArray.class); 316 } 317 318 319 /** 320 * Gets a list member of a JSON object. 321 * 322 * @param o The JSON object. Must not be {@code null}. 323 * @param key The JSON object member key. Must not be {@code null}. 324 * 325 * @return The member value. 326 * 327 * @throws ParseException If the value is missing, {@code null} or not 328 * of the expected type. 329 */ 330 @SuppressWarnings("unchecked") 331 public static List<Object> getList(final JSONObject o, final String key) 332 throws ParseException { 333 334 return getGeneric(o, key, List.class); 335 } 336 337 338 /** 339 * Gets a string array member of a JSON object. 340 * 341 * @param o The JSON object. Must not be {@code null}. 342 * @param key The JSON object member key. Must not be {@code null}. 343 * 344 * @return The member value. 345 * 346 * @throws ParseException If the value is missing, {@code null} or not 347 * of the expected type. 348 */ 349 public static String[] getStringArray(final JSONObject o, final String key) 350 throws ParseException { 351 352 List<Object> list = getList(o, key); 353 354 try { 355 return list.toArray(new String[0]); 356 357 } catch (ArrayStoreException e) { 358 359 throw new ParseException("JSON object member with key \"" + key + "\" is not an array of strings"); 360 } 361 } 362 363 364 /** 365 * Gets a string array member of a JSON object as a string set. 366 * 367 * @param o The JSON object. Must not be {@code null}. 368 * @param key The JSON object member key. Must not be {@code null}. 369 * 370 * @return The member value. 371 * 372 * @throws ParseException If the value is missing, {@code null} or not 373 * of the expected type. 374 */ 375 public static Set<String> getStringSet(final JSONObject o, final String key) 376 throws ParseException { 377 378 List<Object> list = getList(o, key); 379 380 Set<String> set = new HashSet<String>(); 381 382 for (Object item: list) { 383 384 try { 385 set.add((String)item); 386 387 } catch (Exception e) { 388 389 throw new ParseException("JSON object member wit key \"" + key + "\" is not an array of strings"); 390 } 391 392 } 393 394 return set; 395 } 396 397 398 /** 399 * Gets a JSON object member of a JSON object. 400 * 401 * @param o The JSON object. Must not be {@code null}. 402 * @param key The JSON object member key. Must not be {@code null}. 403 * 404 * @return The member value. 405 * 406 * @throws ParseException If the value is missing, {@code null} or not 407 * of the expected type. 408 */ 409 public static JSONObject getJSONObject(final JSONObject o, final String key) 410 throws ParseException { 411 412 return getGeneric(o, key, JSONObject.class); 413 } 414 415 416 /** 417 * Prevents instantiation. 418 */ 419 private JSONObjectUtils() { 420 421 // Nothing to do 422 } 423} 424