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