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.oauth2.sdk.ParseException;
031
032
033/**
034 * JSON object helper methods for parsing and typed retrieval of member values.
035 */
036public final class JSONObjectUtils {
037        
038        
039        /**
040         * Returns {@code true} if the JSON object is defined and contains the 
041         * specified key.
042         *
043         * @param jsonObject The JSON object to check. May be {@code null}.
044         * @param key        The key to check. Must not be {@code null}.
045         *
046         * @return {@code true} if the JSON object is defined and contains the
047         *         specified key, else {@code false}.
048         */
049        public static boolean containsKey(final JSONObject jsonObject, final String key) {
050
051                return jsonObject != null && jsonObject.containsKey(key);
052        }
053        
054        
055        /**
056         * Parses a JSON object.
057         *
058         * <p>Specific JSON to Java entity mapping (as per JSON Simple):
059         *
060         * <ul>
061         *     <li>JSON numbers mapped to {@code java.lang.Number}.
062         *     <li>JSON integer numbers mapped to {@code long}.
063         *     <li>JSON fraction numbers mapped to {@code double}.
064         * </ul>
065         *
066         * @param s The JSON object string to parse. Must not be {@code null}.
067         *
068         * @return The JSON object.
069         *
070         * @throws ParseException If the string cannot be parsed to a JSON 
071         *                        object.
072         */
073        public static JSONObject parse(final String s)
074                throws ParseException {
075                
076                Object o = JSONUtils.parseJSON(s);
077                
078                if (o instanceof JSONObject)
079                        return (JSONObject)o;
080                else
081                        throw new ParseException("The JSON entity is not an object");
082        }
083        
084        
085        /**
086         * Parses a JSON object while keeping the order of JSON object members.
087         *
088         * <p>Specific JSON to Java entity mapping (as per JSON Simple):
089         *
090         * <ul>
091         *     <li>JSON numbers mapped to {@code java.lang.Number}.
092         *     <li>JSON integer numbers mapped to {@code long}.
093         *     <li>JSON fraction numbers mapped to {@code double}.
094         * </ul>
095         *
096         * @param s The JSON object string to parse. Must not be {@code null}.
097         *
098         * @return The JSON object as linked hash map.
099         *
100         * @throws ParseException If the string cannot be parsed to a JSON
101         *                        object.
102         */
103        public static LinkedHashMap<String,Object> parseKeepingOrder(final String s)
104                throws ParseException {
105                
106                Object o = JSONUtils.parseJSONKeepingOrder(s);
107                
108                if (o instanceof LinkedHashMap)
109                        return (LinkedHashMap<String,Object>)o;
110                else
111                        throw new ParseException("The JSON entity is not an object");
112        }
113
114
115        /**
116         * Use {@link #parse(String)} instead.
117         *
118         * @param s The JSON object string to parse. Must not be {@code null}.
119         *
120         * @return The JSON object.
121         *
122         * @throws ParseException If the string cannot be parsed to a JSON
123         *                        object.
124         */
125        @Deprecated
126        public static JSONObject parseJSONObject(final String s)
127                throws ParseException {
128
129                return parse(s);
130        }
131        
132        
133        /**
134         * Gets a generic member of a JSON object.
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         * @param clazz The expected class of the JSON object member value. Must
139         *              not be {@code null}.
140         *
141         * @return The JSON object member value.
142         *
143         * @throws ParseException If the value is missing, {@code null} or not
144         *                        of the expected type.
145         */
146        public static <T> T getGeneric(final JSONObject o, final String key, final Class<T> clazz)
147                throws ParseException {
148        
149                if (! o.containsKey(key))
150                        throw new ParseException("Missing JSON object member with key \"" + key + "\"");
151                
152                Object value = o.get(key);
153                
154                if (value == null) {
155                        throw new ParseException("JSON object member with key \"" + key + "\" has null value");
156                }
157                
158                try {
159                        return JSONUtils.to(value, clazz);
160                } catch (ParseException e) {
161                        throw new ParseException("Unexpected type of JSON object member with key \"" + key + "\"", e);
162                }
163        }
164
165
166        /**
167         * Gets a boolean member of a JSON object.
168         *
169         * @param o   The JSON object. Must not be {@code null}.
170         * @param key The JSON object member key. Must not be {@code null}.
171         *
172         * @return The member value.
173         *
174         * @throws ParseException If the value is missing, {@code null} or not
175         *                        of the expected type.
176         */
177        public static boolean getBoolean(final JSONObject o, final String key)
178                throws ParseException {
179                
180                return getGeneric(o, key, Boolean.class);
181        }
182
183
184        /**
185         * Gets a boolean member of a JSON object.
186         *
187         * @param o   The JSON object. Must not be {@code null}.
188         * @param key The JSON object member key. Must not be {@code null}.
189         * @param def The default value to return if the key is not present or.
190         *            the value is {@code null}. May be {@code null}.
191         *
192         * @return The member value.
193         *
194         * @throws ParseException If the value is not of the expected type.
195         */
196        public static boolean getBoolean(final JSONObject o, final String key, final boolean def)
197                throws ParseException {
198                
199                if (o.get(key) != null) {
200                        return getBoolean(o, key);
201                }
202                
203                return def;
204        }
205        
206        
207        /**
208         * Gets an number member of a JSON object as {@code int}.
209         *
210         * @param o   The JSON object. Must not be {@code null}.
211         * @param key The JSON object member key. Must not be {@code null}.
212         *
213         * @return The member value.
214         *
215         * @throws ParseException If the value is missing, {@code null} or not
216         *                        of the expected type.
217         */
218        public static int getInt(final JSONObject o, final String key)
219                throws ParseException {
220                
221                return getGeneric(o, key, Number.class).intValue();     
222        }
223        
224        
225        /**
226         * Gets an number member of a JSON object as {@code int}.
227         *
228         * @param o   The JSON object. Must not be {@code null}.
229         * @param key The JSON object member key. Must not be {@code null}.
230         * @param def The default value to return if the key is not present or
231         *            the value is {@code null}.
232         *
233         * @return The member value.
234         *
235         * @throws ParseException If the value is not of the expected type.
236         */
237        public static int getInt(final JSONObject o, final String key, final int def)
238                throws ParseException {
239                
240                if (o.get(key) != null) {
241                        return getInt(o, key);
242                }
243                
244                return def;
245        }
246        
247        
248        /**
249         * Gets a number member of a JSON object as {@code long}.
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 member value.
255         *
256         * @throws ParseException If the value is missing, {@code null} or not
257         *                        of the expected type.
258         */
259        public static long getLong(final JSONObject o, final String key)
260                throws ParseException {
261                
262                return getGeneric(o, key, Number.class).longValue();
263        }
264        
265        
266        /**
267         * Gets a number member of a JSON object as {@code long}.
268         *
269         * @param o   The JSON object. Must not be {@code null}.
270         * @param key The JSON object member key. Must not be {@code null}.
271         * @param def The default value to return if the key is not present or
272         *            the value is {@code null}.
273         *
274         * @return The member value.
275         *
276         * @throws ParseException If the value is not of the expected type.
277         */
278        public static long getLong(final JSONObject o, final String key, final long def)
279                throws ParseException {
280                
281                if (o.get(key) != null) {
282                        return getLong(o, key);
283                }
284                
285                return def;
286        }
287        
288        
289        /**
290         * Gets a number member of a JSON object {@code float}.
291         *
292         * @param o   The JSON object. Must not be {@code null}.
293         * @param key The JSON object member key. Must not be {@code null}.
294         *
295         * @return The member value.
296         *
297         * @throws ParseException If the value is missing, {@code null} or not
298         *                        of the expected type.
299         */
300        public static float getFloat(final JSONObject o, final String key)
301                throws ParseException {
302                
303                return getGeneric(o, key, Number.class).floatValue();
304        }
305        
306        
307        /**
308         * Gets a number member of a JSON object {@code float}.
309         *
310         * @param o   The JSON object. Must not be {@code null}.
311         * @param key The JSON object member key. Must not be {@code null}.
312         * @param def The default value to return if the key is not present or
313         *            the value is {@code null}.
314         *
315         * @return The member value.
316         *
317         * @throws ParseException If the value is not of the expected type.
318         */
319        public static float getFloat(final JSONObject o, final String key, final float def)
320                throws ParseException {
321                
322                if (o.get(key) != null) {
323                        return getFloat(o, key);
324                }
325                
326                return def;
327        }
328        
329        
330        /**
331         * Gets a number member of a JSON object as {@code double}.
332         *
333         * @param o   The JSON object. Must not be {@code null}.
334         * @param key The JSON object member key. Must not be {@code null}.
335         *
336         * @return The member value.
337         *
338         * @throws ParseException If the value is missing, {@code null} or not
339         *                        of the expected type.
340         */
341        public static double getDouble(final JSONObject o, final String key)
342                throws ParseException {
343                
344                return getGeneric(o, key, Number.class).doubleValue();
345        }
346        
347        
348        /**
349         * Gets a number member of a JSON object as {@code double}.
350         *
351         * @param o   The JSON object. Must not be {@code null}.
352         * @param key The JSON object member key. Must not be {@code null}.
353         * @param def The default value to return if the key is not present or
354         *            the value is {@code null}.
355         *
356         * @return The member value.
357         *
358         * @throws ParseException If the value is not of the expected type.
359         */
360        public static double getDouble(final JSONObject o, final String key, final double def)
361                throws ParseException {
362                
363                if (o.get(key) != null) {
364                        return getDouble(o, key);
365                }
366                
367                return def;
368        }
369
370
371        /**
372         * Gets a number member of a JSON object as {@code java.lang.Number}.
373         *
374         * @param o   The JSON object. Must not be {@code null}.
375         * @param key The JSON object member key. Must not be {@code null}.
376         *
377         * @return The member value.
378         *
379         * @throws ParseException If the value is missing, {@code null} or not
380         *                        of the expected type.
381         */
382        public static Number getNumber(final JSONObject o, final String key)
383                throws ParseException {
384
385                return getGeneric(o, key, Number.class);
386        }
387
388
389        /**
390         * Gets a number member of a JSON object as {@code java.lang.Number}.
391         *
392         * @param o   The JSON object. Must not be {@code null}.
393         * @param key The JSON object member key. Must not be {@code null}.
394         * @param def The default value to return if the key is not present or
395         *            the value is {@code null}. May be {@code null}.
396         *
397         * @return The member value.
398         *
399         * @throws ParseException If the value is not of the expected type.
400         */
401        public static Number getNumber(final JSONObject o, final String key, final Number def)
402                throws ParseException {
403
404                if (o.get(key) != null) {
405                        return getNumber(o, key);
406                }
407                
408                return def;
409        }
410        
411        
412        /**
413         * Gets a string member of a JSON object.
414         *
415         * @param o   The JSON object. Must not be {@code null}.
416         * @param key The JSON object member key. Must not be {@code null}.
417         *
418         * @return The member value.
419         *
420         * @throws ParseException If the value is missing, {@code null} or not
421         *                        of the expected type.
422         */
423        public static String getString(final JSONObject o, final String key)
424                throws ParseException {
425                
426                return getGeneric(o, key, String.class);
427        }
428        
429        
430        /**
431         * Gets a string member of a JSON object.
432         *
433         * @param o   The JSON object. Must not be {@code null}.
434         * @param key The JSON object member key. Must not be {@code null}.
435         * @param def The default value to return if the key is not present or
436         *            the value is {@code null}. May be {@code null}.
437         *
438         * @return The member value.
439         *
440         * @throws ParseException If the value is not of the expected type.
441         */
442        public static String getString(final JSONObject o, final String key, final String def)
443                throws ParseException {
444                
445                if (o.get(key) != null) {
446                        return getString(o, key);
447                }
448                
449                return def;
450        }
451
452
453        /**
454         * Gets a string member of a JSON object as an enumerated object.
455         *
456         * @param o         The JSON object. Must not be {@code null}.
457         * @param key       The JSON object member key. Must not be
458         *                  {@code null}.
459         * @param enumClass The enumeration class. Must not be {@code null}.
460         *
461         * @return The member value.
462         *
463         * @throws ParseException If the value is missing, {@code null} or not
464         *                        of the expected type.
465         */
466        public static <T extends Enum<T>> T getEnum(final JSONObject o, 
467                                                    final String key,
468                                                    final Class<T> enumClass)
469                throws ParseException {
470
471                String value = getString(o, key);
472
473                for (T en: enumClass.getEnumConstants()) {
474                               
475                        if (en.toString().equalsIgnoreCase(value))
476                                return en;
477                }
478
479                throw new ParseException("Unexpected value of JSON object member with key \"" + key + "\"");
480        }
481
482
483        /**
484         * Gets a string member of a JSON object as an enumerated object.
485         *
486         * @param o         The JSON object. Must not be {@code null}.
487         * @param key       The JSON object member key. Must not be
488         *                  {@code null}.
489         * @param enumClass The enumeration class. Must not be {@code null}.
490         * @param def       The default value to return if the key is not
491         *                  present or the value is {@code null}. May be
492         *                  {@code null}.
493         *
494         * @return The member value.
495         *
496         * @throws ParseException If the value is not of the expected type.
497         */
498        public static <T extends Enum<T>> T getEnum(final JSONObject o,
499                                                    final String key,
500                                                    final Class<T> enumClass,
501                                                    final T def)
502                throws ParseException {
503
504                if (o.get(key) != null) {
505                        return getEnum(o, key, enumClass);
506                }
507                
508                return def;
509        }
510
511
512        /**
513         * Gets a string member of a JSON object as {@code java.net.URI}.
514         *
515         * @param o   The JSON object. Must not be {@code null}.
516         * @param key The JSON object member key. Must not be {@code null}.
517         *
518         * @return The member value.
519         *
520         * @throws ParseException If the value is missing, {@code null} or not
521         *                        of the expected type.
522         */
523        public static URI getURI(final JSONObject o, final String key)
524                throws ParseException {
525
526                try {
527                        return new URI(getGeneric(o, key, String.class));
528
529                } catch (URISyntaxException e) {
530
531                        throw new ParseException(e.getMessage(), e);
532                }
533        }
534
535
536        /**
537         * Gets a string member of a JSON object as {@code java.net.URI}.
538         *
539         * @param o   The JSON object. Must not be {@code null}.
540         * @param key The JSON object member key. Must not be {@code null}.
541         * @param def The default value to return if the key is not present or
542         *            the value is {@code null}. May be {@code null}.
543         *
544         * @return The member value.
545         *
546         * @throws ParseException If the value is not of the expected type.
547         */
548        public static URI getURI(final JSONObject o, final String key, final URI def)
549                throws ParseException {
550
551                if (o.get(key) != null) {
552                        return getURI(o, key);
553                }
554                
555                return def;
556        }
557        
558        
559        /**
560         * Gets a string member of a JSON object as {@code java.net.URL}.
561         *
562         * @param o   The JSON object. Must not be {@code null}.
563         * @param key The JSON object member key. Must not be {@code null}.
564         *
565         * @return The member value.
566         *
567         * @throws ParseException If the value is missing, {@code null} or not
568         *                        of the expected type.
569         */
570        public static URL getURL(final JSONObject o, final String key)
571                throws ParseException {
572                
573                try {
574                        return new URL(getGeneric(o, key, String.class));
575                        
576                } catch (MalformedURLException e) {
577                
578                        throw new ParseException(e.getMessage(), e);
579                }
580        }
581        
582        
583        /**
584         * Gets a JSON array member of a JSON object.
585         *
586         * @param o   The JSON object. Must not be {@code null}.
587         * @param key The JSON object member key. Must not be {@code null}.
588         *
589         * @return The member value.
590         *
591         * @throws ParseException If the value is missing, {@code null} or not
592         *                        of the expected type.
593         */
594        public static JSONArray getJSONArray(final JSONObject o, final String key)
595                throws ParseException {
596                
597                return getGeneric(o, key, JSONArray.class);
598        }
599        
600        
601        /**
602         * Gets a JSON array member of a JSON object.
603         *
604         * @param o   The JSON object. Must not be {@code null}.
605         * @param key The JSON object member key. Must not be {@code null}.
606         * @param def The default value to return if the key is not present or
607         *            the value is {@code null}. May be {@code null}.
608         *
609         * @return The member value.
610         *
611         * @throws ParseException If the value is not of the expected type.
612         */
613        public static JSONArray getJSONArray(final JSONObject o, final String key, final JSONArray def)
614                throws ParseException {
615                
616                if (o.get(key) != null) {
617                        return getJSONArray(o, key);
618                }
619                
620                return def;
621        }
622
623
624        /**
625         * Gets a list member of a JSON object.
626         *
627         * @param o   The JSON object. Must not be {@code null}.
628         * @param key The JSON object member key. Must not be {@code null}.
629         *
630         * @return The member value.
631         *
632         * @throws ParseException If the value is missing, {@code null} or not
633         *                        of the expected type.
634         */
635        @SuppressWarnings("unchecked")
636        public static List<Object> getList(final JSONObject o, final String key)
637                throws ParseException {
638                
639                return getGeneric(o, key, List.class);
640        }
641
642
643        /**
644         * Gets a list member of a JSON object.
645         *
646         * @param o   The JSON object. Must not be {@code null}.
647         * @param key The JSON object member key. Must not be {@code null}.
648         * @param def The default value to return if the key is not present or
649         *            the value is {@code null}. May be {@code null}.
650         *
651         * @return The member value.
652         *
653         * @throws ParseException If the value is not of the expected type.
654         */
655        @SuppressWarnings("unchecked")
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         * Prevents public instantiation.
857         */
858        private JSONObjectUtils() {}
859}
860