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