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.*; 026 027import net.minidev.json.JSONArray; 028import net.minidev.json.JSONObject; 029 030import com.nimbusds.jwt.JWTClaimsSet; 031import com.nimbusds.oauth2.sdk.ParseException; 032 033 034/** 035 * JSON object helper methods for parsing and typed retrieval of member values. 036 */ 037public final class JSONObjectUtils { 038 039 040 /** 041 * Returns {@code true} if the JSON object is defined and contains the 042 * specified key. 043 * 044 * @param jsonObject The JSON object to check. May be {@code null}. 045 * @param key The key to check. Must not be {@code null}. 046 * 047 * @return {@code true} if the JSON object is defined and contains the 048 * specified key, else {@code false}. 049 */ 050 public static boolean containsKey(final JSONObject jsonObject, final String key) { 051 052 return jsonObject != null && jsonObject.containsKey(key); 053 } 054 055 056 /** 057 * Parses a JSON object. 058 * 059 * <p>Specific JSON to Java entity mapping (as per JSON Simple): 060 * 061 * <ul> 062 * <li>JSON numbers mapped to {@code java.lang.Number}. 063 * <li>JSON integer numbers mapped to {@code long}. 064 * <li>JSON fraction numbers mapped to {@code double}. 065 * </ul> 066 * 067 * @param s The JSON object string to parse. Must not be {@code null}. 068 * 069 * @return The JSON object. 070 * 071 * @throws ParseException If the string cannot be parsed to a JSON 072 * object. 073 */ 074 public static JSONObject parse(final String s) 075 throws ParseException { 076 077 Object o = JSONUtils.parseJSON(s); 078 079 if (o instanceof JSONObject) 080 return (JSONObject)o; 081 else 082 throw new ParseException("The JSON entity is not an object"); 083 } 084 085 086 /** 087 * Parses a JSON object while keeping the order of JSON object members. 088 * 089 * <p>Specific JSON to Java entity mapping (as per JSON Simple): 090 * 091 * <ul> 092 * <li>JSON numbers mapped to {@code java.lang.Number}. 093 * <li>JSON integer numbers mapped to {@code long}. 094 * <li>JSON fraction numbers mapped to {@code double}. 095 * </ul> 096 * 097 * @param s The JSON object string to parse. Must not be {@code null}. 098 * 099 * @return The JSON object as linked hash map. 100 * 101 * @throws ParseException If the string cannot be parsed to a JSON 102 * object. 103 */ 104 public static LinkedHashMap<String,Object> parseKeepingOrder(final String s) 105 throws ParseException { 106 107 Object o = JSONUtils.parseJSONKeepingOrder(s); 108 109 if (o instanceof LinkedHashMap) 110 return (LinkedHashMap<String,Object>)o; 111 else 112 throw new ParseException("The JSON entity is not an object"); 113 } 114 115 116 /** 117 * Use {@link #parse(String)} instead. 118 * 119 * @param s The JSON object string to parse. Must not be {@code null}. 120 * 121 * @return The JSON object. 122 * 123 * @throws ParseException If the string cannot be parsed to a JSON 124 * object. 125 */ 126 @Deprecated 127 public static JSONObject parseJSONObject(final String s) 128 throws ParseException { 129 130 return parse(s); 131 } 132 133 134 /** 135 * Gets a generic 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 * @param clazz The expected class of the JSON object member value. Must 140 * not be {@code null}. 141 * 142 * @return The JSON object member value. 143 * 144 * @throws ParseException If the value is missing, {@code null} or not 145 * of the expected type. 146 */ 147 public static <T> T getGeneric(final JSONObject o, final String key, final Class<T> clazz) 148 throws ParseException { 149 150 if (! o.containsKey(key)) 151 throw new ParseException("Missing JSON object member with key " + key + ""); 152 153 Object value = o.get(key); 154 155 if (value == null) { 156 throw new ParseException("JSON object member with key " + key + " has null value"); 157 } 158 159 try { 160 return JSONUtils.to(value, clazz); 161 } catch (ParseException e) { 162 throw new ParseException("Unexpected type of JSON object member with key " + key + "", e); 163 } 164 } 165 166 167 /** 168 * Gets a boolean member of a JSON object. 169 * 170 * @param o The JSON object. Must not be {@code null}. 171 * @param key The JSON object member key. Must not be {@code null}. 172 * 173 * @return The member value. 174 * 175 * @throws ParseException If the value is missing, {@code null} or not 176 * of the expected type. 177 */ 178 public static boolean getBoolean(final JSONObject o, final String key) 179 throws ParseException { 180 181 return getGeneric(o, key, Boolean.class); 182 } 183 184 185 /** 186 * Gets a boolean member of a JSON object. 187 * 188 * @param o The JSON object. Must not be {@code null}. 189 * @param key The JSON object member key. Must not be {@code null}. 190 * @param def The default value to return if the key is not present or. 191 * the value is {@code null}. May be {@code null}. 192 * 193 * @return The member value. 194 * 195 * @throws ParseException If the value is not of the expected type. 196 */ 197 public static boolean getBoolean(final JSONObject o, final String key, final boolean def) 198 throws ParseException { 199 200 if (o.get(key) != null) { 201 return getBoolean(o, key); 202 } 203 204 return def; 205 } 206 207 208 /** 209 * Gets an number member of a JSON object as {@code int}. 210 * 211 * @param o The JSON object. Must not be {@code null}. 212 * @param key The JSON object member key. Must not be {@code null}. 213 * 214 * @return The member value. 215 * 216 * @throws ParseException If the value is missing, {@code null} or not 217 * of the expected type. 218 */ 219 public static int getInt(final JSONObject o, final String key) 220 throws ParseException { 221 222 return getGeneric(o, key, Number.class).intValue(); 223 } 224 225 226 /** 227 * Gets an number member of a JSON object as {@code int}. 228 * 229 * @param o The JSON object. Must not be {@code null}. 230 * @param key The JSON object member key. Must not be {@code null}. 231 * @param def The default value to return if the key is not present or 232 * the value is {@code null}. 233 * 234 * @return The member value. 235 * 236 * @throws ParseException If the value is not of the expected type. 237 */ 238 public static int getInt(final JSONObject o, final String key, final int def) 239 throws ParseException { 240 241 if (o.get(key) != null) { 242 return getInt(o, key); 243 } 244 245 return def; 246 } 247 248 249 /** 250 * Gets a number member of a JSON object as {@code long}. 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 long getLong(final JSONObject o, final String key) 261 throws ParseException { 262 263 return getGeneric(o, key, Number.class).longValue(); 264 } 265 266 267 /** 268 * Gets a number member of a JSON object as {@code long}. 269 * 270 * @param o The JSON object. Must not be {@code null}. 271 * @param key The JSON object member key. Must not be {@code null}. 272 * @param def The default value to return if the key is not present or 273 * the value is {@code null}. 274 * 275 * @return The member value. 276 * 277 * @throws ParseException If the value is not of the expected type. 278 */ 279 public static long getLong(final JSONObject o, final String key, final long def) 280 throws ParseException { 281 282 if (o.get(key) != null) { 283 return getLong(o, key); 284 } 285 286 return def; 287 } 288 289 290 /** 291 * Gets a number member of a JSON object {@code float}. 292 * 293 * @param o The JSON object. Must not be {@code null}. 294 * @param key The JSON object member key. Must not be {@code null}. 295 * 296 * @return The member value. 297 * 298 * @throws ParseException If the value is missing, {@code null} or not 299 * of the expected type. 300 */ 301 public static float getFloat(final JSONObject o, final String key) 302 throws ParseException { 303 304 return getGeneric(o, key, Number.class).floatValue(); 305 } 306 307 308 /** 309 * Gets a number member of a JSON object {@code float}. 310 * 311 * @param o The JSON object. Must not be {@code null}. 312 * @param key The JSON object member key. Must not be {@code null}. 313 * @param def The default value to return if the key is not present or 314 * the value is {@code null}. 315 * 316 * @return The member value. 317 * 318 * @throws ParseException If the value is not of the expected type. 319 */ 320 public static float getFloat(final JSONObject o, final String key, final float def) 321 throws ParseException { 322 323 if (o.get(key) != null) { 324 return getFloat(o, key); 325 } 326 327 return def; 328 } 329 330 331 /** 332 * Gets a number member of a JSON object as {@code double}. 333 * 334 * @param o The JSON object. Must not be {@code null}. 335 * @param key The JSON object member key. Must not be {@code null}. 336 * 337 * @return The member value. 338 * 339 * @throws ParseException If the value is missing, {@code null} or not 340 * of the expected type. 341 */ 342 public static double getDouble(final JSONObject o, final String key) 343 throws ParseException { 344 345 return getGeneric(o, key, Number.class).doubleValue(); 346 } 347 348 349 /** 350 * Gets a number member of a JSON object as {@code double}. 351 * 352 * @param o The JSON object. Must not be {@code null}. 353 * @param key The JSON object member key. Must not be {@code null}. 354 * @param def The default value to return if the key is not present or 355 * the value is {@code null}. 356 * 357 * @return The member value. 358 * 359 * @throws ParseException If the value is not of the expected type. 360 */ 361 public static double getDouble(final JSONObject o, final String key, final double def) 362 throws ParseException { 363 364 if (o.get(key) != null) { 365 return getDouble(o, key); 366 } 367 368 return def; 369 } 370 371 372 /** 373 * Gets a number member of a JSON object as {@code java.lang.Number}. 374 * 375 * @param o The JSON object. Must not be {@code null}. 376 * @param key The JSON object member key. Must not be {@code null}. 377 * 378 * @return The member value. 379 * 380 * @throws ParseException If the value is missing, {@code null} or not 381 * of the expected type. 382 */ 383 public static Number getNumber(final JSONObject o, final String key) 384 throws ParseException { 385 386 return getGeneric(o, key, Number.class); 387 } 388 389 390 /** 391 * Gets a number member of a JSON object as {@code java.lang.Number}. 392 * 393 * @param o The JSON object. Must not be {@code null}. 394 * @param key The JSON object member key. Must not be {@code null}. 395 * @param def The default value to return if the key is not present or 396 * the value is {@code null}. May be {@code null}. 397 * 398 * @return The member value. 399 * 400 * @throws ParseException If the value is not of the expected type. 401 */ 402 public static Number getNumber(final JSONObject o, final String key, final Number def) 403 throws ParseException { 404 405 if (o.get(key) != null) { 406 return getNumber(o, key); 407 } 408 409 return def; 410 } 411 412 413 /** 414 * Gets a string member of a JSON object. 415 * 416 * @param o The JSON object. Must not be {@code null}. 417 * @param key The JSON object member key. Must not be {@code null}. 418 * 419 * @return The member value. 420 * 421 * @throws ParseException If the value is missing, {@code null} or not 422 * of the expected type. 423 */ 424 public static String getString(final JSONObject o, final String key) 425 throws ParseException { 426 427 return getGeneric(o, key, String.class); 428 } 429 430 431 /** 432 * Gets a string member of a JSON object. 433 * 434 * @param o The JSON object. Must not be {@code null}. 435 * @param key The JSON object member key. Must not be {@code null}. 436 * @param def The default value to return if the key is not present or 437 * the value is {@code null}. May be {@code null}. 438 * 439 * @return The member value. 440 * 441 * @throws ParseException If the value is not of the expected type. 442 */ 443 public static String getString(final JSONObject o, final String key, final String def) 444 throws ParseException { 445 446 if (o.get(key) != null) { 447 return getString(o, key); 448 } 449 450 return def; 451 } 452 453 454 /** 455 * Gets a string member of a JSON object as an enumerated object. 456 * 457 * @param o The JSON object. Must not be {@code null}. 458 * @param key The JSON object member key. Must not be 459 * {@code null}. 460 * @param enumClass The enumeration class. Must not be {@code null}. 461 * 462 * @return The member value. 463 * 464 * @throws ParseException If the value is missing, {@code null} or not 465 * of the expected type. 466 */ 467 public static <T extends Enum<T>> T getEnum(final JSONObject o, 468 final String key, 469 final Class<T> enumClass) 470 throws ParseException { 471 472 String value = getString(o, key); 473 474 for (T en: enumClass.getEnumConstants()) { 475 476 if (en.toString().equalsIgnoreCase(value)) 477 return en; 478 } 479 480 throw new ParseException("Unexpected value of JSON object member with key " + key + ""); 481 } 482 483 484 /** 485 * Gets a string member of a JSON object as an enumerated object. 486 * 487 * @param o The JSON object. Must not be {@code null}. 488 * @param key The JSON object member key. Must not be 489 * {@code null}. 490 * @param enumClass The enumeration class. Must not be {@code null}. 491 * @param def The default value to return if the key is not 492 * present or the value is {@code null}. May be 493 * {@code null}. 494 * 495 * @return The member value. 496 * 497 * @throws ParseException If the value is not of the expected type. 498 */ 499 public static <T extends Enum<T>> T getEnum(final JSONObject o, 500 final String key, 501 final Class<T> enumClass, 502 final T def) 503 throws ParseException { 504 505 if (o.get(key) != null) { 506 return getEnum(o, key, enumClass); 507 } 508 509 return def; 510 } 511 512 513 /** 514 * Gets a string member of a JSON object as {@code java.net.URI}. 515 * 516 * @param o The JSON object. Must not be {@code null}. 517 * @param key The JSON object member key. Must not be {@code null}. 518 * 519 * @return The member value. 520 * 521 * @throws ParseException If the value is missing, {@code null} or not 522 * of the expected type. 523 */ 524 public static URI getURI(final JSONObject o, final String key) 525 throws ParseException { 526 527 try { 528 return new URI(getGeneric(o, key, String.class)); 529 530 } catch (URISyntaxException e) { 531 532 throw new ParseException(e.getMessage(), e); 533 } 534 } 535 536 537 /** 538 * Gets a string member of a JSON object as {@code java.net.URI}. 539 * 540 * @param o The JSON object. Must not be {@code null}. 541 * @param key The JSON object member key. Must not be {@code null}. 542 * @param def The default value to return if the key is not present or 543 * the value is {@code null}. May be {@code null}. 544 * 545 * @return The member value. 546 * 547 * @throws ParseException If the value is not of the expected type. 548 */ 549 public static URI getURI(final JSONObject o, final String key, final URI def) 550 throws ParseException { 551 552 if (o.get(key) != null) { 553 return getURI(o, key); 554 } 555 556 return def; 557 } 558 559 560 /** 561 * Gets a string member of a JSON object as {@code java.net.URL}. 562 * 563 * @param o The JSON object. Must not be {@code null}. 564 * @param key The JSON object member key. Must not be {@code null}. 565 * 566 * @return The member value. 567 * 568 * @throws ParseException If the value is missing, {@code null} or not 569 * of the expected type. 570 */ 571 public static URL getURL(final JSONObject o, final String key) 572 throws ParseException { 573 574 try { 575 return new URL(getGeneric(o, key, String.class)); 576 577 } catch (MalformedURLException e) { 578 579 throw new ParseException(e.getMessage(), e); 580 } 581 } 582 583 584 /** 585 * Gets a JSON array member of a JSON object. 586 * 587 * @param o The JSON object. Must not be {@code null}. 588 * @param key The JSON object member key. Must not be {@code null}. 589 * 590 * @return The member value. 591 * 592 * @throws ParseException If the value is missing, {@code null} or not 593 * of the expected type. 594 */ 595 public static JSONArray getJSONArray(final JSONObject o, final String key) 596 throws ParseException { 597 598 return getGeneric(o, key, JSONArray.class); 599 } 600 601 602 /** 603 * Gets a JSON array member of a JSON object. 604 * 605 * @param o The JSON object. Must not be {@code null}. 606 * @param key The JSON object member key. Must not be {@code null}. 607 * @param def The default value to return if the key is not present or 608 * the value is {@code null}. May be {@code null}. 609 * 610 * @return The member value. 611 * 612 * @throws ParseException If the value is not of the expected type. 613 */ 614 public static JSONArray getJSONArray(final JSONObject o, final String key, final JSONArray def) 615 throws ParseException { 616 617 if (o.get(key) != null) { 618 return getJSONArray(o, key); 619 } 620 621 return def; 622 } 623 624 625 /** 626 * Gets a list member of a JSON object. 627 * 628 * @param o The JSON object. Must not be {@code null}. 629 * @param key The JSON object member key. Must not be {@code null}. 630 * 631 * @return The member value. 632 * 633 * @throws ParseException If the value is missing, {@code null} or not 634 * of the expected type. 635 */ 636 @SuppressWarnings("unchecked") 637 public static List<Object> getList(final JSONObject o, final String key) 638 throws ParseException { 639 640 return getGeneric(o, key, List.class); 641 } 642 643 644 /** 645 * Gets a list member of a JSON object. 646 * 647 * @param o The JSON object. Must not be {@code null}. 648 * @param key The JSON object member key. Must not be {@code null}. 649 * @param def The default value to return if the key is not present or 650 * the value is {@code null}. May be {@code null}. 651 * 652 * @return The member value. 653 * 654 * @throws ParseException If the value is not of the expected type. 655 */ 656 public static List<Object> getList(final JSONObject o, final String key, final List<Object> def) 657 throws ParseException { 658 659 if (o.get(key) != null) { 660 return getList(o, key); 661 } 662 663 return def; 664 } 665 666 667 /** 668 * Gets a string array member of a JSON object. 669 * 670 * @param o The JSON object. Must not be {@code null}. 671 * @param key The JSON object member key. Must not be {@code null}. 672 * 673 * @return The member value. 674 * 675 * @throws ParseException If the value is missing, {@code null} or not 676 * of the expected type. 677 */ 678 public static String[] getStringArray(final JSONObject o, final String key) 679 throws ParseException { 680 681 List<Object> list = getList(o, key); 682 683 try { 684 return list.toArray(new String[0]); 685 686 } catch (ArrayStoreException e) { 687 688 throw new ParseException("JSON object member with key " + key + " is not an array of strings"); 689 } 690 } 691 692 693 /** 694 * Gets a string array member of a JSON object. 695 * 696 * @param o The JSON object. Must not be {@code null}. 697 * @param key The JSON object member key. Must not be {@code null}. 698 * @param def The default value to return if the key is not present or 699 * the value is {@code null}. May be {@code null}. 700 * 701 * @return The member value. 702 * 703 * @throws ParseException If the value is not of the expected type. 704 */ 705 public static String[] getStringArray(final JSONObject o, final String key, final String[] def) 706 throws ParseException { 707 708 if (o.get(key) != null) { 709 return getStringArray(o, key); 710 } 711 712 return def; 713 } 714 715 716 /** 717 * Gets a string list member of a JSON object. 718 * 719 * @param o The JSON object. Must not be {@code null}. 720 * @param key The JSON object member key. Must not be {@code null}. 721 * 722 * @return The member value. 723 * 724 * @throws ParseException If the value is missing, {@code null} or not 725 * of the expected type. 726 */ 727 public static List<String> getStringList(final JSONObject o, final String key) 728 throws ParseException { 729 730 return Arrays.asList(getStringArray(o, key)); 731 } 732 733 734 /** 735 * Gets a string list member of a JSON object. 736 * 737 * @param o The JSON object. Must not be {@code null}. 738 * @param key The JSON object member key. Must not be {@code null}. 739 * @param def The default value to return if the key is not present or 740 * the value is {@code null}. May be {@code null}. 741 * 742 * @return The member value. 743 * 744 * @throws ParseException If the value is not of the expected type. 745 */ 746 public static List<String> getStringList(final JSONObject o, final String key, final List<String> def) 747 throws ParseException { 748 749 if (o.get(key) != null) { 750 return getStringList(o, key); 751 } 752 753 return def; 754 } 755 756 757 /** 758 * Gets a string array member of a JSON object as a string set. 759 * 760 * @param o The JSON object. Must not be {@code null}. 761 * @param key The JSON object member key. Must not be {@code null}. 762 * 763 * @return The member value. 764 * 765 * @throws ParseException If the value is missing, {@code null} or not 766 * of the expected type. 767 */ 768 public static Set<String> getStringSet(final JSONObject o, final String key) 769 throws ParseException { 770 771 List<Object> list = getList(o, key); 772 773 Set<String> set = new HashSet<>(); 774 775 for (Object item: list) { 776 777 try { 778 set.add((String)item); 779 780 } catch (Exception e) { 781 782 throw new ParseException("JSON object member with key " + key + " is not an array of strings"); 783 } 784 785 } 786 787 return set; 788 } 789 790 791 /** 792 * Gets a string array member of a JSON object as a string set. 793 * 794 * @param o The JSON object. Must not be {@code null}. 795 * @param key The JSON object member key. Must not be {@code null}. 796 * @param def The default value to return if the key is not present or 797 * the value is {@code null}. May be {@code null}. 798 * 799 * @return The member value. 800 * 801 * @throws ParseException If the value is not of the expected type. 802 */ 803 public static Set<String> getStringSet(final JSONObject o, final String key, final Set<String> def) 804 throws ParseException { 805 806 if (o.get(key) != null) { 807 return getStringSet(o, key); 808 } 809 810 return def; 811 } 812 813 814 /** 815 * Gets a JSON object member of a JSON object. 816 * 817 * @param o The JSON object. Must not be {@code null}. 818 * @param key The JSON object member key. Must not be {@code null}. 819 * 820 * @return The member value. 821 * 822 * @throws ParseException If the value is missing, {@code null} or not 823 * of the expected type. 824 */ 825 public static JSONObject getJSONObject(final JSONObject o, final String key) 826 throws ParseException { 827 828 return getGeneric(o, key, JSONObject.class); 829 } 830 831 832 /** 833 * Gets a JSON object member of a JSON object. 834 * 835 * @param o The JSON object. Must not be {@code null}. 836 * @param key The JSON object member key. Must not be {@code null}. 837 * @param def The default value to return if the key is not present or 838 * the value is {@code null}. May be {@code null}. 839 * 840 * @return The member value. 841 * 842 * @throws ParseException If the value is not of the expected type. 843 */ 844 public static JSONObject getJSONObject(final JSONObject o, final String key, final JSONObject def) 845 throws ParseException { 846 847 if (o.get(key) != null) { 848 return getJSONObject(o, key); 849 } 850 851 return def; 852 } 853 854 855 /** 856 * Returns the JSON object representation of the specified JWT claims 857 * set. 858 * 859 * @param jwtClaimsSet The JWT claims set, {@code null} if not 860 * specified. 861 * 862 * @return The JSON object, {@code null} if not specified. 863 */ 864 public static JSONObject toJSONObject(final JWTClaimsSet jwtClaimsSet) { 865 866 if (jwtClaimsSet == null) { 867 return null; 868 } 869 870 if (jwtClaimsSet.getClaims().isEmpty()) { 871 return new JSONObject(); 872 } 873 874 // Serialise and parse is the safest method 875 final String json = jwtClaimsSet.toString(); 876 877 try { 878 return parse(json); 879 } catch (ParseException e) { 880 // Should never happen 881 return null; 882 } 883 } 884 885 886 /** 887 * Prevents public instantiation. 888 */ 889 private JSONObjectUtils() {} 890} 891