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 * Use {@link #parse(String)} instead. 076 */ 077 @Deprecated 078 public static JSONObject parseJSONObject(final String s) 079 throws ParseException { 080 081 return parse(s); 082 } 083 084 085 /** 086 * Gets a generic member of a JSON object. 087 * 088 * @param o The JSON object. Must not be {@code null}. 089 * @param key The JSON object member key. Must not be {@code null}. 090 * @param clazz The expected class of the JSON object member value. Must 091 * not be {@code null}. 092 * 093 * @return The JSON object member value. 094 * 095 * @throws ParseException If the value is missing, {@code null} or not 096 * of the expected type. 097 */ 098 @SuppressWarnings("unchecked") 099 public static <T> T getGeneric(final JSONObject o, final String key, final Class<T> clazz) 100 throws ParseException { 101 102 if (! o.containsKey(key)) 103 throw new ParseException("Missing JSON object member with key \"" + key + "\""); 104 105 if (o.get(key) == null) 106 throw new ParseException("JSON object member with key \"" + key + "\" has null value"); 107 108 Object value = o.get(key); 109 110 if (! clazz.isAssignableFrom(value.getClass())) 111 throw new ParseException("Unexpected type of JSON object member with key \"" + key + "\""); 112 113 return (T)value; 114 } 115 116 117 /** 118 * Gets a boolean member of a JSON object. 119 * 120 * @param o The JSON object. Must not be {@code null}. 121 * @param key The JSON object member key. Must not be {@code null}. 122 * 123 * @return The member value. 124 * 125 * @throws ParseException If the value is missing, {@code null} or not 126 * of the expected type. 127 */ 128 public static boolean getBoolean(final JSONObject o, final String key) 129 throws ParseException { 130 131 return getGeneric(o, key, Boolean.class); 132 } 133 134 135 /** 136 * Gets an number member of a JSON object as {@code int}. 137 * 138 * @param o The JSON object. Must not be {@code null}. 139 * @param key The JSON object member key. Must not be {@code null}. 140 * 141 * @return The member value. 142 * 143 * @throws ParseException If the value is missing, {@code null} or not 144 * of the expected type. 145 */ 146 public static int getInt(final JSONObject o, final String key) 147 throws ParseException { 148 149 return getGeneric(o, key, Number.class).intValue(); 150 } 151 152 153 /** 154 * Gets a number member of a JSON object as {@code long}. 155 * 156 * @param o The JSON object. Must not be {@code null}. 157 * @param key The JSON object member key. Must not be {@code null}. 158 * 159 * @return The member value. 160 * 161 * @throws ParseException If the value is missing, {@code null} or not 162 * of the expected type. 163 */ 164 public static long getLong(final JSONObject o, final String key) 165 throws ParseException { 166 167 return getGeneric(o, key, Number.class).longValue(); 168 } 169 170 171 /** 172 * Gets a number member of a JSON object {@code float}. 173 * 174 * @param o The JSON object. Must not be {@code null}. 175 * @param key The JSON object member key. Must not be {@code null}. 176 * 177 * @return The member value. 178 * 179 * @throws ParseException If the value is missing, {@code null} or not 180 * of the expected type. 181 */ 182 public static float getFloat(final JSONObject o, final String key) 183 throws ParseException { 184 185 return getGeneric(o, key, Number.class).floatValue(); 186 } 187 188 189 /** 190 * Gets a number member of a JSON object as {@code double}. 191 * 192 * @param o The JSON object. Must not be {@code null}. 193 * @param key The JSON object member key. Must not be {@code null}. 194 * 195 * @return The member value. 196 * 197 * @throws ParseException If the value is missing, {@code null} or not 198 * of the expected type. 199 */ 200 public static double getDouble(final JSONObject o, final String key) 201 throws ParseException { 202 203 return getGeneric(o, key, Number.class).doubleValue(); 204 } 205 206 207 /** 208 * Gets a number member of a JSON object as {@code java.lang.Number}. 209 * 210 * @param o The JSON object. Must not be {@code null}. 211 * @param key The JSON object member key. Must not be {@code null}. 212 * 213 * @return The member value. 214 * 215 * @throws ParseException If the value is missing, {@code null} or not 216 * of the expected type. 217 */ 218 public static Number getNumber(final JSONObject o, final String key) 219 throws ParseException { 220 221 return getGeneric(o, key, Number.class); 222 } 223 224 225 /** 226 * Gets a string member of a JSON object. 227 * 228 * @param o The JSON object. Must not be {@code null}. 229 * @param key The JSON object member key. Must not be {@code null}. 230 * 231 * @return The member value. 232 * 233 * @throws ParseException If the value is missing, {@code null} or not 234 * of the expected type. 235 */ 236 public static String getString(final JSONObject o, final String key) 237 throws ParseException { 238 239 return getGeneric(o, key, String.class); 240 } 241 242 243 /** 244 * Gets a string member of a JSON object as an enumerated object. 245 * 246 * @param o The JSON object. Must not be {@code null}. 247 * @param key The JSON object member key. Must not be 248 * {@code null}. 249 * @param enumClass The enumeration class. Must not be {@code null}. 250 * 251 * @return The member value. 252 * 253 * @throws ParseException If the value is missing, {@code null} or not 254 * of the expected type. 255 */ 256 public static <T extends Enum<T>> T getEnum(final JSONObject o, 257 final String key, 258 final Class<T> enumClass) 259 throws ParseException { 260 261 String value = getString(o, key); 262 263 for (T en: enumClass.getEnumConstants()) { 264 265 if (en.toString().equalsIgnoreCase(value)) 266 return en; 267 } 268 269 throw new ParseException("Unexpected value of JSON object member with key \"" + key + "\""); 270 } 271 272 273 /** 274 * Gets a string member of a JSON object as {@code java.net.URI}. 275 * 276 * @param o The JSON object. Must not be {@code null}. 277 * @param key The JSON object member key. Must not be {@code null}. 278 * 279 * @return The member value. 280 * 281 * @throws ParseException If the value is missing, {@code null} or not 282 * of the expected type. 283 */ 284 public static URI getURI(final JSONObject o, final String key) 285 throws ParseException { 286 287 try { 288 return new URI(getGeneric(o, key, String.class)); 289 290 } catch (URISyntaxException e) { 291 292 throw new ParseException(e.getMessage(), e); 293 } 294 } 295 296 297 /** 298 * Gets a string member of a JSON object as {@code java.net.URL}. 299 * 300 * @param o The JSON object. Must not be {@code null}. 301 * @param key The JSON object member key. Must not be {@code null}. 302 * 303 * @return The member value. 304 * 305 * @throws ParseException If the value is missing, {@code null} or not 306 * of the expected type. 307 */ 308 public static URL getURL(final JSONObject o, final String key) 309 throws ParseException { 310 311 try { 312 return new URL(getGeneric(o, key, String.class)); 313 314 } catch (MalformedURLException e) { 315 316 throw new ParseException(e.getMessage(), e); 317 } 318 } 319 320 321 /** 322 * Gets a string member of a JSON object as 323 * {@code javax.mail.internet.InternetAddress}. 324 * 325 * @param o The JSON object. Must not be {@code null}. 326 * @param key The JSON object member key. Must not be {@code null}. 327 * 328 * @return The member value. 329 * 330 * @throws ParseException If the value is missing, {@code null} or not 331 * of the expected type. 332 */ 333 public static InternetAddress getEmail(final JSONObject o, final String key) 334 throws ParseException { 335 336 try { 337 final boolean strict = true; 338 339 return new InternetAddress(getGeneric(o, key, String.class), strict); 340 341 } catch (AddressException e) { 342 343 throw new ParseException(e.getMessage(), e); 344 } 345 } 346 347 348 /** 349 * Gets a JSON array member of a JSON object. 350 * 351 * @param o The JSON object. Must not be {@code null}. 352 * @param key The JSON object member key. Must not be {@code null}. 353 * 354 * @return The member value. 355 * 356 * @throws ParseException If the value is missing, {@code null} or not 357 * of the expected type. 358 */ 359 public static JSONArray getJSONArray(final JSONObject o, final String key) 360 throws ParseException { 361 362 return getGeneric(o, key, JSONArray.class); 363 } 364 365 366 /** 367 * Gets a list member of a JSON object. 368 * 369 * @param o The JSON object. Must not be {@code null}. 370 * @param key The JSON object member key. Must not be {@code null}. 371 * 372 * @return The member value. 373 * 374 * @throws ParseException If the value is missing, {@code null} or not 375 * of the expected type. 376 */ 377 @SuppressWarnings("unchecked") 378 public static List<Object> getList(final JSONObject o, final String key) 379 throws ParseException { 380 381 return getGeneric(o, key, List.class); 382 } 383 384 385 /** 386 * Gets a string array member of a JSON object. 387 * 388 * @param o The JSON object. Must not be {@code null}. 389 * @param key The JSON object member key. Must not be {@code null}. 390 * 391 * @return The member value. 392 * 393 * @throws ParseException If the value is missing, {@code null} or not 394 * of the expected type. 395 */ 396 public static String[] getStringArray(final JSONObject o, final String key) 397 throws ParseException { 398 399 List<Object> list = getList(o, key); 400 401 try { 402 return list.toArray(new String[0]); 403 404 } catch (ArrayStoreException e) { 405 406 throw new ParseException("JSON object member with key \"" + key + "\" is not an array of strings"); 407 } 408 } 409 410 411 /** 412 * Gets a string list member of a JSON object. 413 * 414 * @param o The JSON object. Must not be {@code null}. 415 * @param key The JSON object member key. Must not be {@code null}. 416 * 417 * @return The member value. 418 * 419 * @throws ParseException If the value is missing, {@code null} or not 420 * of the expected type. 421 */ 422 public static List<String> getStringList(final JSONObject o, final String key) 423 throws ParseException { 424 425 return Arrays.asList(getStringArray(o, key)); 426 } 427 428 429 /** 430 * Gets a string array member of a JSON object as a string set. 431 * 432 * @param o The JSON object. Must not be {@code null}. 433 * @param key The JSON object member key. Must not be {@code null}. 434 * 435 * @return The member value. 436 * 437 * @throws ParseException If the value is missing, {@code null} or not 438 * of the expected type. 439 */ 440 public static Set<String> getStringSet(final JSONObject o, final String key) 441 throws ParseException { 442 443 List<Object> list = getList(o, key); 444 445 Set<String> set = new HashSet<>(); 446 447 for (Object item: list) { 448 449 try { 450 set.add((String)item); 451 452 } catch (Exception e) { 453 454 throw new ParseException("JSON object member wit key \"" + key + "\" is not an array of strings"); 455 } 456 457 } 458 459 return set; 460 } 461 462 463 /** 464 * Gets a JSON object member of a JSON object. 465 * 466 * @param o The JSON object. Must not be {@code null}. 467 * @param key The JSON object member key. Must not be {@code null}. 468 * 469 * @return The member value. 470 * 471 * @throws ParseException If the value is missing, {@code null} or not 472 * of the expected type. 473 */ 474 public static JSONObject getJSONObject(final JSONObject o, final String key) 475 throws ParseException { 476 477 return getGeneric(o, key, JSONObject.class); 478 } 479 480 481 /** 482 * Prevents instantiation. 483 */ 484 private JSONObjectUtils() { 485 486 // Nothing to do 487 } 488} 489