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