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