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