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.
163         *            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 (containsKey(o, key)) {
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.
204         *
205         * @return The member value.
206         *
207         * @throws ParseException If the value is not of the expected type.
208         */
209        public static int getInt(final JSONObject o, final String key, final int def)
210                throws ParseException {
211                
212                if (containsKey(o, key)) {
213                        return getInt(o, key);
214                }
215                
216                return def;
217        }
218        
219        
220        /**
221         * Gets a number member of a JSON object as {@code long}.
222         *
223         * @param o   The JSON object. Must not be {@code null}.
224         * @param key The JSON object member key. Must not be {@code null}.
225         *
226         * @return The member value.
227         *
228         * @throws ParseException If the value is missing, {@code null} or not
229         *                        of the expected type.
230         */
231        public static long getLong(final JSONObject o, final String key)
232                throws ParseException {
233                
234                return getGeneric(o, key, Number.class).longValue();
235        }
236        
237        
238        /**
239         * Gets a number member of a JSON object as {@code long}.
240         *
241         * @param o   The JSON object. Must not be {@code null}.
242         * @param key The JSON object member key. Must not be {@code null}.
243         * @param def The default value to return if the key is not present.
244         *
245         * @return The member value.
246         *
247         * @throws ParseException If the value is not of the expected type.
248         */
249        public static long getLong(final JSONObject o, final String key, final long def)
250                throws ParseException {
251                
252                if (containsKey(o, key)) {
253                        return getLong(o, key);
254                }
255                
256                return def;
257        }
258        
259        
260        /**
261         * Gets a number member of a JSON object {@code float}.
262         *
263         * @param o   The JSON object. Must not be {@code null}.
264         * @param key The JSON object member key. Must not be {@code null}.
265         *
266         * @return The member value.
267         *
268         * @throws ParseException If the value is missing, {@code null} or not
269         *                        of the expected type.
270         */
271        public static float getFloat(final JSONObject o, final String key)
272                throws ParseException {
273                
274                return getGeneric(o, key, Number.class).floatValue();
275        }
276        
277        
278        /**
279         * Gets a number member of a JSON object {@code float}.
280         *
281         * @param o   The JSON object. Must not be {@code null}.
282         * @param key The JSON object member key. Must not be {@code null}.
283         * @param def The default value to return if the key is not present.
284         *
285         * @return The member value.
286         *
287         * @throws ParseException If the value is not of the expected type.
288         */
289        public static float getFloat(final JSONObject o, final String key, final float def)
290                throws ParseException {
291                
292                if (containsKey(o, key)) {
293                        return getFloat(o, key);
294                }
295                
296                return def;
297        }
298        
299        
300        /**
301         * Gets a number member of a JSON object as {@code double}.
302         *
303         * @param o   The JSON object. Must not be {@code null}.
304         * @param key The JSON object member key. Must not be {@code null}.
305         *
306         * @return The member value.
307         *
308         * @throws ParseException If the value is missing, {@code null} or not
309         *                        of the expected type.
310         */
311        public static double getDouble(final JSONObject o, final String key)
312                throws ParseException {
313                
314                return getGeneric(o, key, Number.class).doubleValue();
315        }
316        
317        
318        /**
319         * Gets a number member of a JSON object as {@code double}.
320         *
321         * @param o   The JSON object. Must not be {@code null}.
322         * @param key The JSON object member key. Must not be {@code null}.
323         * @param def The default value to return if the key is not present.
324         *
325         * @return The member value.
326         *
327         * @throws ParseException If the value is not of the expected type.
328         */
329        public static double getDouble(final JSONObject o, final String key, final double def)
330                throws ParseException {
331                
332                if (containsKey(o, key)) {
333                        return getDouble(o, key);
334                }
335                
336                return def;
337        }
338
339
340        /**
341         * Gets a number member of a JSON object as {@code java.lang.Number}.
342         *
343         * @param o   The JSON object. Must not be {@code null}.
344         * @param key The JSON object member key. Must not be {@code null}.
345         *
346         * @return The member value.
347         *
348         * @throws ParseException If the value is missing, {@code null} or not
349         *                        of the expected type.
350         */
351        public static Number getNumber(final JSONObject o, final String key)
352                throws ParseException {
353
354                return getGeneric(o, key, Number.class);
355        }
356
357
358        /**
359         * Gets a number member of a JSON object as {@code java.lang.Number}.
360         *
361         * @param o   The JSON object. Must not be {@code null}.
362         * @param key The JSON object member key. Must not be {@code null}.
363         * @param def The default value to return if the key is not present.
364         *            May be {@code null}.
365         *
366         * @return The member value.
367         *
368         * @throws ParseException If the value is not of the expected type.
369         */
370        public static Number getNumber(final JSONObject o, final String key, final Number def)
371                throws ParseException {
372
373                if (containsKey(o, key)) {
374                        return getNumber(o, key);
375                }
376                
377                return def;
378        }
379        
380        
381        /**
382         * Gets a string member of a JSON object.
383         *
384         * @param o   The JSON object. Must not be {@code null}.
385         * @param key The JSON object member key. Must not be {@code null}.
386         *
387         * @return The member value.
388         *
389         * @throws ParseException If the value is missing, {@code null} or not
390         *                        of the expected type.
391         */
392        public static String getString(final JSONObject o, final String key)
393                throws ParseException {
394                
395                return getGeneric(o, key, String.class);
396        }
397        
398        
399        /**
400         * Gets a string member of a JSON object.
401         *
402         * @param o   The JSON object. Must not be {@code null}.
403         * @param key The JSON object member key. Must not be {@code null}.
404         * @param def The default value to return if the key is not present.
405         *            May be {@code null}.
406         *
407         * @return The member value.
408         *
409         * @throws ParseException If the value is not of the expected type.
410         */
411        public static String getString(final JSONObject o, final String key, final String def)
412                throws ParseException {
413                
414                if (containsKey(o, key)) {
415                        return getString(o, key);
416                }
417                
418                return def;
419        }
420
421
422        /**
423         * Gets a string member of a JSON object as an enumerated object.
424         *
425         * @param o         The JSON object. Must not be {@code null}.
426         * @param key       The JSON object member key. Must not be
427         *                  {@code null}.
428         * @param enumClass The enumeration class. Must not be {@code null}.
429         *
430         * @return The member value.
431         *
432         * @throws ParseException If the value is missing, {@code null} or not
433         *                        of the expected type.
434         */
435        public static <T extends Enum<T>> T getEnum(final JSONObject o, 
436                                                    final String key,
437                                                    final Class<T> enumClass)
438                throws ParseException {
439
440                String value = getString(o, key);
441
442                for (T en: enumClass.getEnumConstants()) {
443                               
444                        if (en.toString().equalsIgnoreCase(value))
445                                return en;
446                }
447
448                throw new ParseException("Unexpected value of JSON object member with key \"" + key + "\"");
449        }
450
451
452        /**
453         * Gets a string member of a JSON object as an enumerated object.
454         *
455         * @param o         The JSON object. Must not be {@code null}.
456         * @param key       The JSON object member key. Must not be
457         *                  {@code null}.
458         * @param enumClass The enumeration class. Must not be {@code null}.
459         * @param def       The default value to return if the key is not
460         *                  present. May be {@code null}.
461         *
462         * @return The member value.
463         *
464         * @throws ParseException If the value is not 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                                                    final T def)
470                throws ParseException {
471
472                if (containsKey(o, key)) {
473                        return getEnum(o, key, enumClass);
474                }
475                
476                return def;
477        }
478
479
480        /**
481         * Gets a string member of a JSON object as {@code java.net.URI}.
482         *
483         * @param o   The JSON object. Must not be {@code null}.
484         * @param key The JSON object member key. Must not be {@code null}.
485         *
486         * @return The member value.
487         *
488         * @throws ParseException If the value is missing, {@code null} or not
489         *                        of the expected type.
490         */
491        public static URI getURI(final JSONObject o, final String key)
492                throws ParseException {
493
494                try {
495                        return new URI(getGeneric(o, key, String.class));
496
497                } catch (URISyntaxException e) {
498
499                        throw new ParseException(e.getMessage(), e);
500                }
501        }
502
503
504        /**
505         * Gets a string member of a JSON object as {@code java.net.URI}.
506         *
507         * @param o   The JSON object. Must not be {@code null}.
508         * @param key The JSON object member key. Must not be {@code null}.
509         * @param def The default value to return if the key is not present.
510         *            May be {@code null}.
511         *
512         * @return The member value.
513         *
514         * @throws ParseException If the value is not of the expected type.
515         */
516        public static URI getURI(final JSONObject o, final String key, final URI def)
517                throws ParseException {
518
519                if (containsKey(o, key)) {
520                        return getURI(o, key);
521                }
522                
523                return def;
524        }
525        
526        
527        /**
528         * Gets a string member of a JSON object as {@code java.net.URL}.
529         *
530         * @param o   The JSON object. Must not be {@code null}.
531         * @param key The JSON object member key. Must not be {@code null}.
532         *
533         * @return The member value.
534         *
535         * @throws ParseException If the value is missing, {@code null} or not
536         *                        of the expected type.
537         */
538        public static URL getURL(final JSONObject o, final String key)
539                throws ParseException {
540                
541                try {
542                        return new URL(getGeneric(o, key, String.class));
543                        
544                } catch (MalformedURLException e) {
545                
546                        throw new ParseException(e.getMessage(), e);
547                }
548        }
549        
550        
551        /**
552         * Gets a string member of a JSON object as 
553         * {@code javax.mail.internet.InternetAddress}.
554         *
555         * @param o   The JSON object. Must not be {@code null}.
556         * @param key The JSON object member key. Must not be {@code null}.
557         *
558         * @return The member value.
559         *
560         * @throws ParseException If the value is missing, {@code null} or not
561         *                        of the expected type.
562         */
563        @Deprecated
564        public static InternetAddress getEmail(final JSONObject o, final String key)
565                throws ParseException {
566                
567                try {
568                        final boolean strict = true;
569                        
570                        return new InternetAddress(getGeneric(o, key, String.class), strict);
571                        
572                } catch (AddressException e) {
573                
574                        throw new ParseException(e.getMessage(), e);
575                }
576        }
577        
578        
579        /**
580         * Gets a JSON array member of a JSON object.
581         *
582         * @param o   The JSON object. Must not be {@code null}.
583         * @param key The JSON object member key. Must not be {@code null}.
584         *
585         * @return The member value.
586         *
587         * @throws ParseException If the value is missing, {@code null} or not
588         *                        of the expected type.
589         */
590        public static JSONArray getJSONArray(final JSONObject o, final String key)
591                throws ParseException {
592                
593                return getGeneric(o, key, JSONArray.class);
594        }
595        
596        
597        /**
598         * Gets a JSON array member of a JSON object.
599         *
600         * @param o   The JSON object. Must not be {@code null}.
601         * @param key The JSON object member key. Must not be {@code null}.
602         * @param def The default value to return if the key is not present.
603         *            May be {@code null}.
604         *
605         * @return The member value.
606         *
607         * @throws ParseException If the value is not of the expected type.
608         */
609        public static JSONArray getJSONArray(final JSONObject o, final String key, final JSONArray def)
610                throws ParseException {
611                
612                if (containsKey(o, key)) {
613                        return getJSONArray(o, key);
614                }
615                
616                return def;
617        }
618
619
620        /**
621         * Gets a list member of a JSON object.
622         *
623         * @param o   The JSON object. Must not be {@code null}.
624         * @param key The JSON object member key. Must not be {@code null}.
625         *
626         * @return The member value.
627         *
628         * @throws ParseException If the value is missing, {@code null} or not
629         *                        of the expected type.
630         */
631        @SuppressWarnings("unchecked")
632        public static List<Object> getList(final JSONObject o, final String key)
633                throws ParseException {
634                
635                return getGeneric(o, key, List.class);
636        }
637
638
639        /**
640         * Gets a list member of a JSON object.
641         *
642         * @param o   The JSON object. Must not be {@code null}.
643         * @param key The JSON object member key. Must not be {@code null}.
644         * @param def The default value to return if the key is not present.
645         *            May be {@code null}.
646         *
647         * @return The member value.
648         *
649         * @throws ParseException If the value is not of the expected type.
650         */
651        @SuppressWarnings("unchecked")
652        public static List<Object> getList(final JSONObject o, final String key, final List<Object> def)
653                throws ParseException {
654                
655                if (containsKey(o, key)) {
656                        return getList(o, key);
657                }
658                
659                return def;
660        }
661
662
663        /**
664         * Gets a string array member of a JSON object.
665         *
666         * @param o   The JSON object. Must not be {@code null}.
667         * @param key The JSON object member key. Must not be {@code null}.
668         *
669         * @return The member value.
670         *
671         * @throws ParseException If the value is missing, {@code null} or not
672         *                        of the expected type.
673         */
674        public static String[] getStringArray(final JSONObject o, final String key)
675                throws ParseException {
676
677                List<Object> list = getList(o, key);
678
679                try {
680                        return list.toArray(new String[0]);
681
682                } catch (ArrayStoreException e) {
683
684                        throw new ParseException("JSON object member with key \"" + key + "\" is not an array of strings");
685                }
686        }
687
688
689        /**
690         * Gets a string array member of a JSON object.
691         *
692         * @param o   The JSON object. Must not be {@code null}.
693         * @param key The JSON object member key. Must not be {@code null}.
694         * @param def The default value to return if the key is not present.
695         *            May be {@code null}.
696         *
697         * @return The member value.
698         *
699         * @throws ParseException If the value is not of the expected type.
700         */
701        public static String[] getStringArray(final JSONObject o, final String key, final String[] def)
702                throws ParseException {
703
704                if (containsKey(o, key)) {
705                        return getStringArray(o, key);
706                }
707                
708                return def;
709        }
710
711
712        /**
713         * Gets a string list member of a JSON object.
714         *
715         * @param o   The JSON object. Must not be {@code null}.
716         * @param key The JSON object member key. Must not be {@code null}.
717         *
718         * @return The member value.
719         *
720         * @throws ParseException If the value is missing, {@code null} or not
721         *                        of the expected type.
722         */
723        public static List<String> getStringList(final JSONObject o, final String key)
724                throws ParseException {
725
726                return Arrays.asList(getStringArray(o, key));
727        }
728
729
730        /**
731         * Gets a string list member of a JSON object.
732         *
733         * @param o   The JSON object. Must not be {@code null}.
734         * @param key The JSON object member key. Must not be {@code null}.
735         * @param def The default value to return if the key is not present.
736         *            May be {@code null}.
737         *
738         * @return The member value.
739         *
740         * @throws ParseException If the value is not of the expected type.
741         */
742        public static List<String> getStringList(final JSONObject o, final String key, final List<String> def)
743                throws ParseException {
744
745                if (containsKey(o, key)) {
746                        return getStringList(o, key);
747                }
748                
749                return def;
750        }
751
752
753        /**
754         * Gets a string array member of a JSON object as a string set.
755         *
756         * @param o   The JSON object. Must not be {@code null}.
757         * @param key The JSON object member key. Must not be {@code null}.
758         *
759         * @return The member value.
760         *
761         * @throws ParseException If the value is missing, {@code null} or not
762         *                        of the expected type.
763         */
764        public static Set<String> getStringSet(final JSONObject o, final String key)
765                throws ParseException {
766
767                List<Object> list = getList(o, key);
768
769                Set<String> set = new HashSet<>();
770
771                for (Object item: list) {
772
773                        try {
774                                set.add((String)item);
775
776                        } catch (Exception e) {
777
778                                throw new ParseException("JSON object member with key \"" + key + "\" is not an array of strings");
779                        }
780
781                }
782
783                return set;
784        }
785
786
787        /**
788         * Gets a string array member of a JSON object as a string set.
789         *
790         * @param o   The JSON object. Must not be {@code null}.
791         * @param key The JSON object member key. Must not be {@code null}.
792         * @param def The default value to return if the key is not present.
793         *            May be {@code null}.
794         *
795         * @return The member value.
796         *
797         * @throws ParseException If the value is not of the expected type.
798         */
799        public static Set<String> getStringSet(final JSONObject o, final String key, final Set<String> def)
800                throws ParseException {
801
802                if (containsKey(o, key)) {
803                        return getStringSet(o, key);
804                }
805                
806                return def;
807        }
808        
809        
810        /**
811         * Gets a JSON object member of a JSON object.
812         *
813         * @param o   The JSON object. Must not be {@code null}.
814         * @param key The JSON object member key. Must not be {@code null}.
815         *
816         * @return The member value.
817         *
818         * @throws ParseException If the value is missing, {@code null} or not
819         *                        of the expected type.
820         */
821        public static JSONObject getJSONObject(final JSONObject o, final String key)
822                throws ParseException {
823                
824                return getGeneric(o, key, JSONObject.class);
825        }
826        
827        
828        /**
829         * Gets a JSON object member of a JSON object.
830         *
831         * @param o   The JSON object. Must not be {@code null}.
832         * @param key The JSON object member key. Must not be {@code null}.
833         * @param def The default value to return if the key is not present.
834         *            May be {@code null}.
835         *
836         * @return The member value.
837         *
838         * @throws ParseException If the value is not of the expected type.
839         */
840        public static JSONObject getJSONObject(final JSONObject o, final String key, final JSONObject def)
841                throws ParseException {
842                
843                if (containsKey(o, key)) {
844                        return getJSONObject(o, key);
845                }
846                
847                return def;
848        }
849        
850
851        /**
852         * Prevents public instantiation.
853         */
854        private JSONObjectUtils() {}
855}
856