001/* 002 * nimbus-jose-jwt 003 * 004 * Copyright 2012-2016, Connect2id Ltd. 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.jose.util; 019 020 021import java.net.URI; 022import java.net.URISyntaxException; 023import java.text.ParseException; 024import java.util.Arrays; 025import java.util.List; 026 027import net.minidev.json.JSONArray; 028import net.minidev.json.JSONObject; 029import net.minidev.json.parser.JSONParser; 030 031 032/** 033 * JSON object helper methods for parsing and typed retrieval of member values. 034 * 035 * @author Vladimir Dzhuvinov 036 * @version 2021-07-01 037 */ 038public class JSONObjectUtils { 039 040 041 /** 042 * Parses a JSON object. 043 * 044 * <p>Specific JSON to Java entity mapping (as per JSON Smart): 045 * 046 * <ul> 047 * <li>JSON true|false map to {@code java.lang.Boolean}. 048 * <li>JSON numbers map to {@code java.lang.Number}. 049 * <ul> 050 * <li>JSON integer numbers map to {@code long}. 051 * <li>JSON fraction numbers map to {@code double}. 052 * </ul> 053 * <li>JSON strings map to {@code java.lang.String}. 054 * <li>JSON arrays map to {@code net.minidev.json.JSONArray}. 055 * <li>JSON objects map to {@code net.minidev.json.JSONObject}. 056 * </ul> 057 * 058 * @param s The JSON object string to parse. Must not be {@code null}. 059 * 060 * @return The JSON object. 061 * 062 * @throws ParseException If the string cannot be parsed to a valid JSON 063 * object. 064 */ 065 public static JSONObject parse(final String s) 066 throws ParseException { 067 068 return parse(s, -1); 069 } 070 071 072 /** 073 * Parses a JSON object with the option to limit the input string size. 074 * 075 * <p>Specific JSON to Java entity mapping (as per JSON Smart): 076 * 077 * <ul> 078 * <li>JSON true|false map to {@code java.lang.Boolean}. 079 * <li>JSON numbers map to {@code java.lang.Number}. 080 * <ul> 081 * <li>JSON integer numbers map to {@code long}. 082 * <li>JSON fraction numbers map to {@code double}. 083 * </ul> 084 * <li>JSON strings map to {@code java.lang.String}. 085 * <li>JSON arrays map to {@code java.util.List<Object>}. 086 * <li>JSON objects map to {@code java.util.Map<String,Object>}. 087 * </ul> 088 * 089 * @param s The JSON object string to parse. Must not be 090 * {@code null}. 091 * @param sizeLimit The max allowed size of the string to parse. A 092 * negative integer means no limit. 093 * 094 * @return The JSON object. 095 * 096 * @throws ParseException If the string cannot be parsed to a valid JSON 097 * object. 098 */ 099 public static JSONObject parse(final String s, final int sizeLimit) 100 throws ParseException { 101 102 if (sizeLimit >= 0 && s.length() > sizeLimit) { 103 throw new ParseException("The parsed string is longer than the max accepted size of " + sizeLimit + " characters", 0); 104 } 105 106 Object o; 107 108 try { 109 o = new JSONParser(JSONParser.USE_HI_PRECISION_FLOAT | JSONParser.ACCEPT_TAILLING_SPACE).parse(s); 110 111 } catch (net.minidev.json.parser.ParseException e) { 112 113 throw new ParseException("Invalid JSON: " + e.getMessage(), 0); 114 } catch (Exception e) { 115 throw new ParseException("Unexpected exception: " + e.getMessage(), 0); 116 } catch (StackOverflowError e) { 117 throw new ParseException("Excessive JSON object and / or array nesting", 0); 118 } 119 120 if (o instanceof JSONObject) { 121 return (JSONObject)o; 122 } else { 123 throw new ParseException("JSON entity is not an object", 0); 124 } 125 } 126 127 128 /** 129 * Use {@link #parse(String)} instead. 130 * 131 * @param s The JSON object string to parse. Must not be {@code null}. 132 * 133 * @return The JSON object. 134 * 135 * @throws ParseException If the string cannot be parsed to a valid JSON 136 * object. 137 */ 138 @Deprecated 139 public static JSONObject parseJSONObject(final String s) 140 throws ParseException { 141 142 return parse(s); 143 } 144 145 146 /** 147 * Gets a generic member of a JSON object. 148 * 149 * @param o The JSON object. Must not be {@code null}. 150 * @param key The JSON object member key. Must not be {@code null}. 151 * @param clazz The expected class of the JSON object member value. Must 152 * not be {@code null}. 153 * 154 * @return The JSON object member value, may be {@code null}. 155 * 156 * @throws ParseException If the value is not of the expected type. 157 */ 158 @SuppressWarnings("unchecked") 159 private static <T> T getGeneric(final JSONObject o, final String key, final Class<T> clazz) 160 throws ParseException { 161 162 if (o.get(key) == null) { 163 return null; 164 } 165 166 Object value = o.get(key); 167 168 if (! clazz.isAssignableFrom(value.getClass())) { 169 throw new ParseException("Unexpected type of JSON object member with key \"" + key + "\"", 0); 170 } 171 172 return (T)value; 173 } 174 175 176 /** 177 * Gets a boolean member of a JSON object. 178 * 179 * @param o The JSON object. Must not be {@code null}. 180 * @param key The JSON object member key. Must not be {@code null}. 181 * 182 * @return The JSON object member value. 183 * 184 * @throws ParseException If the member is missing, the value is 185 * {@code null} or not of the expected type. 186 */ 187 public static boolean getBoolean(final JSONObject o, final String key) 188 throws ParseException { 189 190 Boolean value = getGeneric(o, key, Boolean.class); 191 192 if (value == null) { 193 throw new ParseException("JSON object member with key \"" + key + "\" is missing or null", 0); 194 } 195 196 return value; 197 } 198 199 200 /** 201 * Gets an number member of a JSON object as {@code int}. 202 * 203 * @param o The JSON object. Must not be {@code null}. 204 * @param key The JSON object member key. Must not be {@code null}. 205 * 206 * @return The JSON object member value. 207 * 208 * @throws ParseException If the member is missing, the value is 209 * {@code null} or not of the expected type. 210 */ 211 public static int getInt(final JSONObject o, final String key) 212 throws ParseException { 213 214 Number value = getGeneric(o, key, Number.class); 215 216 if (value == null) { 217 throw new ParseException("JSON object member with key \"" + key + "\" is missing or null", 0); 218 } 219 220 return value.intValue(); 221 } 222 223 224 /** 225 * Gets a number member of a JSON object as {@code long}. 226 * 227 * @param o The JSON object. Must not be {@code null}. 228 * @param key The JSON object member key. Must not be {@code null}. 229 * 230 * @return The JSON object member value. 231 * 232 * @throws ParseException If the member is missing, the value is 233 * {@code null} or not of the expected type. 234 */ 235 public static long getLong(final JSONObject o, final String key) 236 throws ParseException { 237 238 Number value = getGeneric(o, key, Number.class); 239 240 if (value == null) { 241 throw new ParseException("JSON object member with key \"" + key + "\" is missing or null", 0); 242 } 243 244 return value.longValue(); 245 } 246 247 248 /** 249 * Gets a number member of a JSON object {@code float}. 250 * 251 * @param o The JSON object. Must not be {@code null}. 252 * @param key The JSON object member key. Must not be {@code null}. 253 * 254 * @return The JSON object member value, may be {@code null}. 255 * 256 * @throws ParseException If the member is missing, the value is 257 * {@code null} or not of the expected type. 258 */ 259 public static float getFloat(final JSONObject o, final String key) 260 throws ParseException { 261 262 Number value = getGeneric(o, key, Number.class); 263 264 if (value == null) { 265 throw new ParseException("JSON object member with key \"" + key + "\" is missing or null", 0); 266 } 267 268 return value.floatValue(); 269 } 270 271 272 /** 273 * Gets a number member of a JSON object as {@code double}. 274 * 275 * @param o The JSON object. Must not be {@code null}. 276 * @param key The JSON object member key. Must not be {@code null}. 277 * 278 * @return The JSON object member value, may be {@code null}. 279 * 280 * @throws ParseException If the member is missing, the value is 281 * {@code null} or not of the expected type. 282 */ 283 public static double getDouble(final JSONObject o, final String key) 284 throws ParseException { 285 286 Number value = getGeneric(o, key, Number.class); 287 288 if (value == null) { 289 throw new ParseException("JSON object member with key \"" + key + "\" is missing or null", 0); 290 } 291 292 return value.doubleValue(); 293 } 294 295 296 /** 297 * Gets a string member of a JSON object. 298 * 299 * @param o The JSON object. Must not be {@code null}. 300 * @param key The JSON object member key. Must not be {@code null}. 301 * 302 * @return The JSON object member value, may be {@code null}. 303 * 304 * @throws ParseException If the value is not of the expected type. 305 */ 306 public static String getString(final JSONObject o, final String key) 307 throws ParseException { 308 309 return getGeneric(o, key, String.class); 310 } 311 312 313 /** 314 * Gets a string member of a JSON object as {@code java.net.URI}. 315 * 316 * @param o The JSON object. Must not be {@code null}. 317 * @param key The JSON object member key. Must not be {@code null}. 318 * 319 * @return The JSON object member value, may be {@code null}. 320 * 321 * @throws ParseException If the value is not of the expected type. 322 */ 323 public static URI getURI(final JSONObject o, final String key) 324 throws ParseException { 325 326 String value = getString(o, key); 327 328 if (value == null) { 329 return null; 330 } 331 332 try { 333 return new URI(value); 334 335 } catch (URISyntaxException e) { 336 337 throw new ParseException(e.getMessage(), 0); 338 } 339 } 340 341 342 /** 343 * Gets a JSON array member of a JSON object. 344 * 345 * @param o The JSON object. Must not be {@code null}. 346 * @param key The JSON object member key. Must not be {@code null}. 347 * 348 * @return The JSON object member value, may be {@code null}. 349 * 350 * @throws ParseException If the value is not of the expected type. 351 */ 352 public static JSONArray getJSONArray(final JSONObject o, final String key) 353 throws ParseException { 354 355 return getGeneric(o, key, JSONArray.class); 356 } 357 358 359 /** 360 * Gets a string array member of a JSON object. 361 * 362 * @param o The JSON object. Must not be {@code null}. 363 * @param key The JSON object member key. Must not be {@code null}. 364 * 365 * @return The JSON object member value, may be {@code null}. 366 * 367 * @throws ParseException If the value is not of the expected type. 368 */ 369 public static String[] getStringArray(final JSONObject o, final String key) 370 throws ParseException { 371 372 JSONArray jsonArray = getJSONArray(o, key); 373 374 if (jsonArray == null) { 375 return null; 376 } 377 378 try { 379 return jsonArray.toArray(new String[0]); 380 381 } catch (ArrayStoreException e) { 382 383 throw new ParseException("JSON object member with key \"" + key + "\" is not an array of strings", 0); 384 } 385 } 386 387 388 /** 389 * Gets a string list member of a JSON object 390 * 391 * @param o The JSON object. Must not be {@code null}. 392 * @param key The JSON object member key. Must not be {@code null}. 393 * 394 * @return The JSON object member value, may be {@code null}. 395 * 396 * @throws ParseException If the value is not of the expected type. 397 */ 398 public static List<String> getStringList(final JSONObject o, final String key) throws ParseException { 399 400 String[] array = getStringArray(o, key); 401 402 if (array == null) { 403 return null; 404 } 405 406 return Arrays.asList(array); 407 } 408 409 410 /** 411 * Gets a JSON object 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 JSON object member value, may be {@code null}. 417 * 418 * @throws ParseException If the value is not of the expected type. 419 */ 420 public static JSONObject getJSONObject(final JSONObject o, final String key) 421 throws ParseException { 422 423 return getGeneric(o, key, JSONObject.class); 424 } 425 426 427 /** 428 * Gets a string member of a JSON object as {@link Base64URL}. 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 JSON object member value, may be {@code null}. 434 * 435 * @throws ParseException If the value is not of the expected type. 436 */ 437 public static Base64URL getBase64URL(final JSONObject o, final String key) 438 throws ParseException { 439 440 String value = getString(o, key); 441 442 if (value == null) { 443 return null; 444 } 445 446 return new Base64URL(value); 447 } 448 449 450 /** 451 * Prevents public instantiation. 452 */ 453 private JSONObjectUtils() { } 454} 455