001/* 002 * oauth2-oidc-sdk 003 * 004 * Copyright 2012-2016, Connect2id Ltd and contributors. 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 007 * this file except in compliance with the License. You may obtain a copy of the 008 * License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software distributed 013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 015 * specific language governing permissions and limitations under the License. 016 */ 017 018package com.nimbusds.oauth2.sdk.util; 019 020 021import java.net.MalformedURLException; 022import java.net.URI; 023import java.net.URISyntaxException; 024import java.net.URL; 025import java.util.Arrays; 026import java.util.HashSet; 027import java.util.List; 028import java.util.Set; 029import javax.mail.internet.AddressException; 030import javax.mail.internet.InternetAddress; 031 032import com.nimbusds.oauth2.sdk.ParseException; 033import net.minidev.json.JSONArray; 034import net.minidev.json.JSONObject; 035 036 037/** 038 * JSON object helper methods for parsing and typed retrieval of member values. 039 */ 040public final class JSONObjectUtils { 041 042 043 /** 044 * Returns {@code true} if the JSON object is defined and contains the 045 * specified key. 046 * 047 * @param jsonObject The JSON object to check. May be {@code null}. 048 * @param key The key to check. Must not be {@code null}. 049 * 050 * @return {@code true} if the JSON object is defined and contains the 051 * specified key, else {@code false}. 052 */ 053 public static boolean containsKey(final JSONObject jsonObject, final String key) { 054 055 return jsonObject != null && jsonObject.containsKey(key); 056 } 057 058 059 /** 060 * Parses a JSON object. 061 * 062 * <p>Specific JSON to Java entity mapping (as per JSON Simple): 063 * 064 * <ul> 065 * <li>JSON numbers mapped to {@code java.lang.Number}. 066 * <li>JSON integer numbers mapped to {@code long}. 067 * <li>JSON fraction numbers mapped to {@code double}. 068 * </ul> 069 * 070 * @param s The JSON object string to parse. Must not be {@code null}. 071 * 072 * @return The JSON object. 073 * 074 * @throws ParseException If the string cannot be parsed to a JSON 075 * object. 076 */ 077 public static JSONObject parse(final String s) 078 throws ParseException { 079 080 Object o = JSONUtils.parseJSON(s); 081 082 if (o instanceof JSONObject) 083 return (JSONObject)o; 084 else 085 throw new ParseException("The JSON entity is not an object"); 086 } 087 088 089 /** 090 * Use {@link #parse(String)} instead. 091 * 092 * @param s The JSON object string to parse. Must not be {@code null}. 093 * 094 * @return The JSON object. 095 * 096 * @throws ParseException If the string cannot be parsed to a JSON 097 * object. 098 */ 099 @Deprecated 100 public static JSONObject parseJSONObject(final String s) 101 throws ParseException { 102 103 return parse(s); 104 } 105 106 107 /** 108 * Gets a generic member of a JSON object. 109 * 110 * @param o The JSON object. Must not be {@code null}. 111 * @param key The JSON object member key. Must not be {@code null}. 112 * @param clazz The expected class of the JSON object member value. Must 113 * not be {@code null}. 114 * 115 * @return The JSON object member value. 116 * 117 * @throws ParseException If the value is missing, {@code null} or not 118 * of the expected type. 119 */ 120 @SuppressWarnings("unchecked") 121 public static <T> T getGeneric(final JSONObject o, final String key, final Class<T> clazz) 122 throws ParseException { 123 124 if (! o.containsKey(key)) 125 throw new ParseException("Missing JSON object member with key \"" + key + "\""); 126 127 if (o.get(key) == null) 128 throw new ParseException("JSON object member with key \"" + key + "\" has null value"); 129 130 Object value = o.get(key); 131 132 if (! clazz.isAssignableFrom(value.getClass())) 133 throw new ParseException("Unexpected type of JSON object member with key \"" + key + "\""); 134 135 return (T)value; 136 } 137 138 139 /** 140 * Gets a boolean member of a JSON object. 141 * 142 * @param o The JSON object. Must not be {@code null}. 143 * @param key The JSON object member key. Must not be {@code null}. 144 * 145 * @return The member value. 146 * 147 * @throws ParseException If the value is missing, {@code null} or not 148 * of the expected type. 149 */ 150 public static boolean getBoolean(final JSONObject o, final String key) 151 throws ParseException { 152 153 return getGeneric(o, key, Boolean.class); 154 } 155 156 157 /** 158 * Gets an number member of a JSON object as {@code int}. 159 * 160 * @param o The JSON object. Must not be {@code null}. 161 * @param key The JSON object member key. Must not be {@code null}. 162 * 163 * @return The member value. 164 * 165 * @throws ParseException If the value is missing, {@code null} or not 166 * of the expected type. 167 */ 168 public static int getInt(final JSONObject o, final String key) 169 throws ParseException { 170 171 return getGeneric(o, key, Number.class).intValue(); 172 } 173 174 175 /** 176 * Gets a number member of a JSON object as {@code long}. 177 * 178 * @param o The JSON object. Must not be {@code null}. 179 * @param key The JSON object member key. Must not be {@code null}. 180 * 181 * @return The member value. 182 * 183 * @throws ParseException If the value is missing, {@code null} or not 184 * of the expected type. 185 */ 186 public static long getLong(final JSONObject o, final String key) 187 throws ParseException { 188 189 return getGeneric(o, key, Number.class).longValue(); 190 } 191 192 193 /** 194 * Gets a number member of a JSON object {@code float}. 195 * 196 * @param o The JSON object. Must not be {@code null}. 197 * @param key The JSON object member key. Must not be {@code null}. 198 * 199 * @return The member value. 200 * 201 * @throws ParseException If the value is missing, {@code null} or not 202 * of the expected type. 203 */ 204 public static float getFloat(final JSONObject o, final String key) 205 throws ParseException { 206 207 return getGeneric(o, key, Number.class).floatValue(); 208 } 209 210 211 /** 212 * Gets a number member of a JSON object as {@code double}. 213 * 214 * @param o The JSON object. Must not be {@code null}. 215 * @param key The JSON object member key. Must not be {@code null}. 216 * 217 * @return The member value. 218 * 219 * @throws ParseException If the value is missing, {@code null} or not 220 * of the expected type. 221 */ 222 public static double getDouble(final JSONObject o, final String key) 223 throws ParseException { 224 225 return getGeneric(o, key, Number.class).doubleValue(); 226 } 227 228 229 /** 230 * Gets a number member of a JSON object as {@code java.lang.Number}. 231 * 232 * @param o The JSON object. Must not be {@code null}. 233 * @param key The JSON object member key. Must not be {@code null}. 234 * 235 * @return The member value. 236 * 237 * @throws ParseException If the value is missing, {@code null} or not 238 * of the expected type. 239 */ 240 public static Number getNumber(final JSONObject o, final String key) 241 throws ParseException { 242 243 return getGeneric(o, key, Number.class); 244 } 245 246 247 /** 248 * Gets a string member of a JSON object. 249 * 250 * @param o The JSON object. Must not be {@code null}. 251 * @param key The JSON object member key. Must not be {@code null}. 252 * 253 * @return The member value. 254 * 255 * @throws ParseException If the value is missing, {@code null} or not 256 * of the expected type. 257 */ 258 public static String getString(final JSONObject o, final String key) 259 throws ParseException { 260 261 return getGeneric(o, key, String.class); 262 } 263 264 265 /** 266 * Gets a string member of a JSON object as an enumerated object. 267 * 268 * @param o The JSON object. Must not be {@code null}. 269 * @param key The JSON object member key. Must not be 270 * {@code null}. 271 * @param enumClass The enumeration class. Must not be {@code null}. 272 * 273 * @return The member value. 274 * 275 * @throws ParseException If the value is missing, {@code null} or not 276 * of the expected type. 277 */ 278 public static <T extends Enum<T>> T getEnum(final JSONObject o, 279 final String key, 280 final Class<T> enumClass) 281 throws ParseException { 282 283 String value = getString(o, key); 284 285 for (T en: enumClass.getEnumConstants()) { 286 287 if (en.toString().equalsIgnoreCase(value)) 288 return en; 289 } 290 291 throw new ParseException("Unexpected value of JSON object member with key \"" + key + "\""); 292 } 293 294 295 /** 296 * Gets a string member of a JSON object as {@code java.net.URI}. 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 URI getURI(final JSONObject o, final String key) 307 throws ParseException { 308 309 try { 310 return new URI(getGeneric(o, key, String.class)); 311 312 } catch (URISyntaxException e) { 313 314 throw new ParseException(e.getMessage(), e); 315 } 316 } 317 318 319 /** 320 * Gets a string member of a JSON object as {@code java.net.URL}. 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 public static URL getURL(final JSONObject o, final String key) 331 throws ParseException { 332 333 try { 334 return new URL(getGeneric(o, key, String.class)); 335 336 } catch (MalformedURLException e) { 337 338 throw new ParseException(e.getMessage(), e); 339 } 340 } 341 342 343 /** 344 * Gets a string member of a JSON object as 345 * {@code javax.mail.internet.InternetAddress}. 346 * 347 * @param o The JSON object. Must not be {@code null}. 348 * @param key The JSON object member key. Must not be {@code null}. 349 * 350 * @return The member value. 351 * 352 * @throws ParseException If the value is missing, {@code null} or not 353 * of the expected type. 354 */ 355 @Deprecated 356 public static InternetAddress getEmail(final JSONObject o, final String key) 357 throws ParseException { 358 359 try { 360 final boolean strict = true; 361 362 return new InternetAddress(getGeneric(o, key, String.class), strict); 363 364 } catch (AddressException e) { 365 366 throw new ParseException(e.getMessage(), e); 367 } 368 } 369 370 371 /** 372 * Gets a JSON array member of a JSON object. 373 * 374 * @param o The JSON object. Must not be {@code null}. 375 * @param key The JSON object member key. Must not be {@code null}. 376 * 377 * @return The member value. 378 * 379 * @throws ParseException If the value is missing, {@code null} or not 380 * of the expected type. 381 */ 382 public static JSONArray getJSONArray(final JSONObject o, final String key) 383 throws ParseException { 384 385 return getGeneric(o, key, JSONArray.class); 386 } 387 388 389 /** 390 * Gets a list member of a JSON object. 391 * 392 * @param o The JSON object. Must not be {@code null}. 393 * @param key The JSON object member key. Must not be {@code null}. 394 * 395 * @return The member value. 396 * 397 * @throws ParseException If the value is missing, {@code null} or not 398 * of the expected type. 399 */ 400 @SuppressWarnings("unchecked") 401 public static List<Object> getList(final JSONObject o, final String key) 402 throws ParseException { 403 404 return getGeneric(o, key, List.class); 405 } 406 407 408 /** 409 * Gets a string array member of a JSON object. 410 * 411 * @param o The JSON object. Must not be {@code null}. 412 * @param key The JSON object member key. Must not be {@code null}. 413 * 414 * @return The member value. 415 * 416 * @throws ParseException If the value is missing, {@code null} or not 417 * of the expected type. 418 */ 419 public static String[] getStringArray(final JSONObject o, final String key) 420 throws ParseException { 421 422 List<Object> list = getList(o, key); 423 424 try { 425 return list.toArray(new String[0]); 426 427 } catch (ArrayStoreException e) { 428 429 throw new ParseException("JSON object member with key \"" + key + "\" is not an array of strings"); 430 } 431 } 432 433 434 /** 435 * Gets a string list member of a JSON object. 436 * 437 * @param o The JSON object. Must not be {@code null}. 438 * @param key The JSON object member key. Must not be {@code null}. 439 * 440 * @return The member value. 441 * 442 * @throws ParseException If the value is missing, {@code null} or not 443 * of the expected type. 444 */ 445 public static List<String> getStringList(final JSONObject o, final String key) 446 throws ParseException { 447 448 return Arrays.asList(getStringArray(o, key)); 449 } 450 451 452 /** 453 * Gets a string array member of a JSON object as a string set. 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 Set<String> getStringSet(final JSONObject o, final String key) 464 throws ParseException { 465 466 List<Object> list = getList(o, key); 467 468 Set<String> set = new HashSet<>(); 469 470 for (Object item: list) { 471 472 try { 473 set.add((String)item); 474 475 } catch (Exception e) { 476 477 throw new ParseException("JSON object member with key \"" + key + "\" is not an array of strings"); 478 } 479 480 } 481 482 return set; 483 } 484 485 486 /** 487 * Gets a string array member of a JSON object as a string set. 488 * 489 * @param o The JSON object. Must not be {@code null}. 490 * @param key The JSON object member key. Must not be {@code null}. 491 * @param def The default value to return if the key is not present. 492 * May be {@code null}. 493 * 494 * @return The member value. 495 * 496 * @throws ParseException If the value is not of the expected type. 497 */ 498 public static Set<String> getStringSet(final JSONObject o, final String key, final Set<String> def) 499 throws ParseException { 500 501 if (containsKey(o, key)) { 502 return getStringSet(o, key); 503 } 504 505 return def; 506 } 507 508 509 /** 510 * Gets a JSON object member of a JSON object. 511 * 512 * @param o The JSON object. Must not be {@code null}. 513 * @param key The JSON object member key. Must not be {@code null}. 514 * 515 * @return The member value. 516 * 517 * @throws ParseException If the value is missing, {@code null} or not 518 * of the expected type. 519 */ 520 public static JSONObject getJSONObject(final JSONObject o, final String key) 521 throws ParseException { 522 523 return getGeneric(o, key, JSONObject.class); 524 } 525 526 527 /** 528 * Gets a JSON object member of a JSON object. 529 * 530 * @param o The JSON object. Must not be {@code null}. 531 * @param key The JSON object member key. Must not be {@code null}. 532 * @param def The default value to return if the key is not present. 533 * May be {@code null}. 534 * 535 * @return The member value. 536 * 537 * @throws ParseException If the value is not of the expected type. 538 */ 539 public static JSONObject getJSONObject(final JSONObject o, final String key, final JSONObject def) 540 throws ParseException { 541 542 if (containsKey(o, key)) { 543 return getJSONObject(o, key); 544 } 545 546 return def; 547 } 548 549 550 /** 551 * Prevents public instantiation. 552 */ 553 private JSONObjectUtils() {} 554} 555