001 package com.nimbusds.oauth2.sdk.util; 002 003 004 import java.net.MalformedURLException; 005 import java.net.URL; 006 import java.util.List; 007 008 import javax.mail.internet.AddressException; 009 import javax.mail.internet.InternetAddress; 010 011 import net.minidev.json.JSONArray; 012 import net.minidev.json.JSONObject; 013 import net.minidev.json.parser.JSONParser; 014 015 import com.nimbusds.oauth2.sdk.ParseException; 016 017 018 /** 019 * JSON object helper methods for parsing and typed retrieval of member values. 020 * 021 * @author Vladimir Dzhuvinov 022 */ 023 public class JSONObjectUtils { 024 025 026 /** 027 * Returns {@code true} if the JSON object is defined and contains the 028 * specified key. 029 * 030 * @param jsonObject The JSON object to check. May be {@code null}. 031 * @param key The key to check. Must not be {@code null}. 032 * 033 * @return {@code true} if the JSON object is defined and contains the 034 * specified key, else {@code false}. 035 */ 036 public static boolean containsKey(final JSONObject jsonObject, final String key) { 037 038 if (jsonObject != null && jsonObject.containsKey(key)) 039 return true; 040 else 041 return false; 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 = null; 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 private 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 string member of a JSON object. 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 String getString(final JSONObject o, final String key) 217 throws ParseException { 218 219 return getGeneric(o, key, String.class); 220 } 221 222 223 /** 224 * Gets a string member of a JSON object as an enumerated object. 225 * 226 * @param o The JSON object. Must not be {@code null}. 227 * @param key The JSON object member key. Must not be 228 * {@code null}. 229 * @param enumClass The enumeration class. 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 <T extends Enum<T>> T getEnum(final JSONObject o, 237 final String key, 238 final Class<T> enumClass) 239 throws ParseException { 240 241 String value = getString(o, key); 242 243 for (T en: enumClass.getEnumConstants()) { 244 245 if (en.toString().equalsIgnoreCase(value)) 246 return en; 247 } 248 249 throw new ParseException("Unexpected value of JSON object member with key \"" + key + "\""); 250 } 251 252 253 /** 254 * Gets a string member of a JSON object as {@code java.net.URL}. 255 * 256 * @param o The JSON object. Must not be {@code null}. 257 * @param key The JSON object member key. Must not be {@code null}. 258 * 259 * @return The member value. 260 * 261 * @throws ParseException If the value is missing, {@code null} or not 262 * of the expected type. 263 */ 264 public static URL getURL(final JSONObject o, final String key) 265 throws ParseException { 266 267 try { 268 return new URL(getGeneric(o, key, String.class)); 269 270 } catch (MalformedURLException e) { 271 272 throw new ParseException(e.getMessage(), e); 273 } 274 } 275 276 277 /** 278 * Gets a string member of a JSON object as 279 * {@code javax.mail.internet.InternetAddress}. 280 * 281 * @param o The JSON object. Must not be {@code null}. 282 * @param key The JSON object member key. Must not be {@code null}. 283 * 284 * @return The member value. 285 * 286 * @throws ParseException If the value is missing, {@code null} or not 287 * of the expected type. 288 */ 289 public static InternetAddress getEmail(final JSONObject o, final String key) 290 throws ParseException { 291 292 try { 293 final boolean strict = true; 294 295 return new InternetAddress(getGeneric(o, key, String.class), strict); 296 297 } catch (AddressException e) { 298 299 throw new ParseException(e.getMessage(), e); 300 } 301 } 302 303 304 /** 305 * Gets a JSON array member of a JSON object. 306 * 307 * @param o The JSON object. Must not be {@code null}. 308 * @param key The JSON object member key. Must not be {@code null}. 309 * 310 * @return The member value. 311 * 312 * @throws ParseException If the value is missing, {@code null} or not 313 * of the expected type. 314 */ 315 public static JSONArray getJSONArray(final JSONObject o, final String key) 316 throws ParseException { 317 318 return getGeneric(o, key, JSONArray.class); 319 } 320 321 322 /** 323 * Gets a list member of a JSON object. 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 @SuppressWarnings("unchecked") 334 public static List<Object> getList(final JSONObject o, final String key) 335 throws ParseException { 336 337 return getGeneric(o, key, List.class); 338 } 339 340 341 /** 342 * Gets a string array member of a JSON object. 343 * 344 * @param o The JSON object. Must not be {@code null}. 345 * @param key The JSON object member key. Must not be {@code null}. 346 * 347 * @return The member value. 348 * 349 * @throws ParseException If the value is missing, {@code null} or not 350 * of the expected type. 351 */ 352 public static String[] getStringArray(final JSONObject o, final String key) 353 throws ParseException { 354 355 List<Object> list = getList(o, key); 356 357 try { 358 return list.toArray(new String[0]); 359 360 } catch (ArrayStoreException e) { 361 362 throw new ParseException("JSON object member with key \"" + key + "\" is not an array of strings"); 363 } 364 } 365 366 367 /** 368 * Gets a JSON object member of a JSON object. 369 * 370 * @param o The JSON object. Must not be {@code null}. 371 * @param key The JSON object member key. Must not be {@code null}. 372 * 373 * @return The member value. 374 * 375 * @throws ParseException If the value is missing, {@code null} or not 376 * of the expected type. 377 */ 378 public static JSONObject getJSONObject(final JSONObject o, final String key) 379 throws ParseException { 380 381 return getGeneric(o, key, JSONObject.class); 382 } 383 384 385 /** 386 * Prevents instantiation. 387 */ 388 private JSONObjectUtils() { 389 390 // Nothing to do 391 } 392 } 393