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