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-06-05 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 } 117 118 if (o instanceof JSONObject) { 119 return (JSONObject)o; 120 } else { 121 throw new ParseException("JSON entity is not an object", 0); 122 } 123 } 124 125 126 /** 127 * Use {@link #parse(String)} instead. 128 * 129 * @param s The JSON object string to parse. Must not be {@code null}. 130 * 131 * @return The JSON object. 132 * 133 * @throws ParseException If the string cannot be parsed to a valid JSON 134 * object. 135 */ 136 @Deprecated 137 public static JSONObject parseJSONObject(final String s) 138 throws ParseException { 139 140 return parse(s); 141 } 142 143 144 /** 145 * Gets a generic member of a JSON object. 146 * 147 * @param o The JSON object. Must not be {@code null}. 148 * @param key The JSON object member key. Must not be {@code null}. 149 * @param clazz The expected class of the JSON object member value. Must 150 * not be {@code null}. 151 * 152 * @return The JSON object member value, may be {@code null}. 153 * 154 * @throws ParseException If the value is not of the expected type. 155 */ 156 @SuppressWarnings("unchecked") 157 private static <T> T getGeneric(final JSONObject o, final String key, final Class<T> clazz) 158 throws ParseException { 159 160 if (o.get(key) == null) { 161 return null; 162 } 163 164 Object value = o.get(key); 165 166 if (! clazz.isAssignableFrom(value.getClass())) { 167 throw new ParseException("Unexpected type of JSON object member with key \"" + key + "\"", 0); 168 } 169 170 return (T)value; 171 } 172 173 174 /** 175 * Gets a boolean member of a JSON object. 176 * 177 * @param o The JSON object. Must not be {@code null}. 178 * @param key The JSON object member key. Must not be {@code null}. 179 * 180 * @return The JSON object member value. 181 * 182 * @throws ParseException If the member is missing, the value is 183 * {@code null} or not of the expected type. 184 */ 185 public static boolean getBoolean(final JSONObject o, final String key) 186 throws ParseException { 187 188 Boolean value = getGeneric(o, key, Boolean.class); 189 190 if (value == null) { 191 throw new ParseException("JSON object member with key \"" + key + "\" is missing or null", 0); 192 } 193 194 return value; 195 } 196 197 198 /** 199 * Gets an number member of a JSON object as {@code int}. 200 * 201 * @param o The JSON object. Must not be {@code null}. 202 * @param key The JSON object member key. Must not be {@code null}. 203 * 204 * @return The JSON object member value. 205 * 206 * @throws ParseException If the member is missing, the value is 207 * {@code null} or not of the expected type. 208 */ 209 public static int getInt(final JSONObject o, final String key) 210 throws ParseException { 211 212 Number value = getGeneric(o, key, Number.class); 213 214 if (value == null) { 215 throw new ParseException("JSON object member with key \"" + key + "\" is missing or null", 0); 216 } 217 218 return value.intValue(); 219 } 220 221 222 /** 223 * Gets a number member of a JSON object as {@code long}. 224 * 225 * @param o The JSON object. Must not be {@code null}. 226 * @param key The JSON object member key. Must not be {@code null}. 227 * 228 * @return The JSON object member value. 229 * 230 * @throws ParseException If the member is missing, the value is 231 * {@code null} or not of the expected type. 232 */ 233 public static long getLong(final JSONObject o, final String key) 234 throws ParseException { 235 236 Number value = getGeneric(o, key, Number.class); 237 238 if (value == null) { 239 throw new ParseException("JSON object member with key \"" + key + "\" is missing or null", 0); 240 } 241 242 return value.longValue(); 243 } 244 245 246 /** 247 * Gets a number member of a JSON object {@code float}. 248 * 249 * @param o The JSON object. Must not be {@code null}. 250 * @param key The JSON object member key. Must not be {@code null}. 251 * 252 * @return The JSON object member value, may be {@code null}. 253 * 254 * @throws ParseException If the member is missing, the value is 255 * {@code null} or not of the expected type. 256 */ 257 public static float getFloat(final JSONObject o, final String key) 258 throws ParseException { 259 260 Number value = getGeneric(o, key, Number.class); 261 262 if (value == null) { 263 throw new ParseException("JSON object member with key \"" + key + "\" is missing or null", 0); 264 } 265 266 return value.floatValue(); 267 } 268 269 270 /** 271 * Gets a number member of a JSON object as {@code double}. 272 * 273 * @param o The JSON object. Must not be {@code null}. 274 * @param key The JSON object member key. Must not be {@code null}. 275 * 276 * @return The JSON object member value, may be {@code null}. 277 * 278 * @throws ParseException If the member is missing, the value is 279 * {@code null} or not of the expected type. 280 */ 281 public static double getDouble(final JSONObject o, final String key) 282 throws ParseException { 283 284 Number value = getGeneric(o, key, Number.class); 285 286 if (value == null) { 287 throw new ParseException("JSON object member with key \"" + key + "\" is missing or null", 0); 288 } 289 290 return value.doubleValue(); 291 } 292 293 294 /** 295 * Gets a string member of a JSON object. 296 * 297 * @param o The JSON object. Must not be {@code null}. 298 * @param key The JSON object member key. Must not be {@code null}. 299 * 300 * @return The JSON object member value, may be {@code null}. 301 * 302 * @throws ParseException If the value is not of the expected type. 303 */ 304 public static String getString(final JSONObject o, final String key) 305 throws ParseException { 306 307 return getGeneric(o, key, String.class); 308 } 309 310 311 /** 312 * Gets a string member of a JSON object as {@code java.net.URI}. 313 * 314 * @param o The JSON object. Must not be {@code null}. 315 * @param key The JSON object member key. Must not be {@code null}. 316 * 317 * @return The JSON object member value, may be {@code null}. 318 * 319 * @throws ParseException If the value is not of the expected type. 320 */ 321 public static URI getURI(final JSONObject o, final String key) 322 throws ParseException { 323 324 String value = getString(o, key); 325 326 if (value == null) { 327 return null; 328 } 329 330 try { 331 return new URI(value); 332 333 } catch (URISyntaxException e) { 334 335 throw new ParseException(e.getMessage(), 0); 336 } 337 } 338 339 340 /** 341 * Gets a JSON array member of a JSON object. 342 * 343 * @param o The JSON object. Must not be {@code null}. 344 * @param key The JSON object member key. Must not be {@code null}. 345 * 346 * @return The JSON object member value, may be {@code null}. 347 * 348 * @throws ParseException If the value is not of the expected type. 349 */ 350 public static JSONArray getJSONArray(final JSONObject o, final String key) 351 throws ParseException { 352 353 return getGeneric(o, key, JSONArray.class); 354 } 355 356 357 /** 358 * Gets a string array member of a JSON object. 359 * 360 * @param o The JSON object. Must not be {@code null}. 361 * @param key The JSON object member key. Must not be {@code null}. 362 * 363 * @return The JSON object member value, may be {@code null}. 364 * 365 * @throws ParseException If the value is not of the expected type. 366 */ 367 public static String[] getStringArray(final JSONObject o, final String key) 368 throws ParseException { 369 370 JSONArray jsonArray = getJSONArray(o, key); 371 372 if (jsonArray == null) { 373 return null; 374 } 375 376 try { 377 return jsonArray.toArray(new String[0]); 378 379 } catch (ArrayStoreException e) { 380 381 throw new ParseException("JSON object member with key \"" + key + "\" is not an array of strings", 0); 382 } 383 } 384 385 386 /** 387 * Gets a string list member of a JSON object 388 * 389 * @param o The JSON object. Must not be {@code null}. 390 * @param key The JSON object member key. Must not be {@code null}. 391 * 392 * @return The JSON object member value, may be {@code null}. 393 * 394 * @throws ParseException If the value is not of the expected type. 395 */ 396 public static List<String> getStringList(final JSONObject o, final String key) throws ParseException { 397 398 String[] array = getStringArray(o, key); 399 400 if (array == null) { 401 return null; 402 } 403 404 return Arrays.asList(array); 405 } 406 407 408 /** 409 * Gets a JSON object 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 JSON object member value, may be {@code null}. 415 * 416 * @throws ParseException If the value is not of the expected type. 417 */ 418 public static JSONObject getJSONObject(final JSONObject o, final String key) 419 throws ParseException { 420 421 return getGeneric(o, key, JSONObject.class); 422 } 423 424 425 /** 426 * Gets a string member of a JSON object as {@link Base64URL}. 427 * 428 * @param o The JSON object. Must not be {@code null}. 429 * @param key The JSON object member key. Must not be {@code null}. 430 * 431 * @return The JSON object member value, may be {@code null}. 432 * 433 * @throws ParseException If the value is not of the expected type. 434 */ 435 public static Base64URL getBase64URL(final JSONObject o, final String key) 436 throws ParseException { 437 438 String value = getString(o, key); 439 440 if (value == null) { 441 return null; 442 } 443 444 return new Base64URL(value); 445 } 446 447 448 /** 449 * Prevents public instantiation. 450 */ 451 private JSONObjectUtils() { } 452} 453