001/*
002 * nimbus-jose-jwt
003 *
004 * Copyright 2012-2016, Connect2id Ltd.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
007 * this file except in compliance with the License. You may obtain a copy of the
008 * License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software distributed
013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
015 * specific language governing permissions and limitations under the License.
016 */
017
018package com.nimbusds.jose.util;
019
020
021import java.net.URI;
022import java.net.URISyntaxException;
023import java.text.ParseException;
024import java.util.Arrays;
025import java.util.List;
026
027import net.minidev.json.JSONArray;
028import net.minidev.json.JSONObject;
029import net.minidev.json.parser.JSONParser;
030
031
032/**
033 * JSON object helper methods for parsing and typed retrieval of member values.
034 *
035 * @author Vladimir Dzhuvinov
036 * @version 2015-04-15
037 */
038public class JSONObjectUtils {
039
040
041        /**
042         * Parses a JSON object.
043         *
044         * <p>Specific JSON to Java entity mapping (as per JSON Smart):
045         *
046         * <ul>
047         *     <li>JSON true|false map to {@code java.lang.Boolean}.
048         *     <li>JSON numbers map to {@code java.lang.Number}.
049         *         <ul>
050         *             <li>JSON integer numbers map to {@code long}.
051         *             <li>JSON fraction numbers map to {@code double}.
052         *         </ul>
053         *     <li>JSON strings map to {@code java.lang.String}.
054         *     <li>JSON arrays map to {@code net.minidev.json.JSONArray}.
055         *     <li>JSON objects map to {@code net.minidev.json.JSONObject}.
056         * </ul>
057         *
058         * @param s The JSON object string to parse. Must not be {@code null}.
059         *
060         * @return The JSON object.
061         *
062         * @throws ParseException If the string cannot be parsed to a valid JSON 
063         *                        object.
064         */
065        public static JSONObject parse(final String s)
066                throws ParseException {
067
068                Object o;
069
070                try {
071                        o = new JSONParser(JSONParser.USE_HI_PRECISION_FLOAT | JSONParser.ACCEPT_TAILLING_SPACE).parse(s);
072
073                } catch (net.minidev.json.parser.ParseException e) {
074
075                        throw new ParseException("Invalid JSON: " + e.getMessage(), 0);
076                }
077
078                if (o instanceof JSONObject) {
079                        return (JSONObject)o;
080                } else {
081                        throw new ParseException("JSON entity is not an object", 0);
082                }
083        }
084
085
086        /**
087         * Use {@link #parse(String)} instead.
088         *
089         * @param s The JSON object string to parse. Must not be {@code null}.
090         *
091         * @return The JSON object.
092         *
093         * @throws ParseException If the string cannot be parsed to a valid JSON
094         *                        object.
095         */
096        @Deprecated
097        public static JSONObject parseJSONObject(final String s)
098                throws ParseException {
099
100                return parse(s);
101        }
102
103
104        /**
105         * Gets a generic member of a JSON object.
106         *
107         * @param o     The JSON object. Must not be {@code null}.
108         * @param key   The JSON object member key. Must not be {@code null}.
109         * @param clazz The expected class of the JSON object member value. Must
110         *              not be {@code null}.
111         *
112         * @return The JSON object member value.
113         *
114         * @throws ParseException If the value is missing, {@code null} or not
115         *                        of the expected type.
116         */
117        @SuppressWarnings("unchecked")
118        private static <T> T getGeneric(final JSONObject o, final String key, final Class<T> clazz)
119                        throws ParseException {
120
121                if (! o.containsKey(key)) {
122                        throw new ParseException("Missing JSON object member with key \"" + key + "\"", 0);
123                }
124
125                if (o.get(key) == null) {
126                        throw new ParseException("JSON object member with key \"" + key + "\" has null value", 0);
127                }
128
129                Object value = o.get(key);
130
131                if (! clazz.isAssignableFrom(value.getClass())) {
132                        throw new ParseException("Unexpected type of JSON object member with key \"" + key + "\"", 0);
133                }
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 an number member of a JSON object as {@code int}.
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         *
163         * @return The member value.
164         *
165         * @throws ParseException If the value is missing, {@code null} or not
166         *                        of the expected type.
167         */
168        public static int getInt(final JSONObject o, final String key)
169                        throws ParseException {
170
171                return getGeneric(o, key, Number.class).intValue();     
172        }
173
174
175        /**
176         * Gets a number member of a JSON object as {@code long}.
177         *
178         * @param o   The JSON object. Must not be {@code null}.
179         * @param key The JSON object member key. Must not be {@code null}.
180         *
181         * @return The member value.
182         *
183         * @throws ParseException If the value is missing, {@code null} or not
184         *                        of the expected type.
185         */
186        public static long getLong(final JSONObject o, final String key)
187                        throws ParseException {
188
189                return getGeneric(o, key, Number.class).longValue();
190        }
191
192
193        /**
194         * Gets a number member of a JSON object {@code float}.
195         *
196         * @param o   The JSON object. Must not be {@code null}.
197         * @param key The JSON object member key. Must not be {@code null}.
198         *
199         * @return The member value.
200         *
201         * @throws ParseException If the value is missing, {@code null} or not
202         *                        of the expected type.
203         */
204        public static float getFloat(final JSONObject o, final String key)
205                        throws ParseException {
206
207                return getGeneric(o, key, Number.class).floatValue();
208        }
209
210
211        /**
212         * Gets a number member of a JSON object as {@code double}.
213         *
214         * @param o   The JSON object. Must not be {@code null}.
215         * @param key The JSON object member key. Must not be {@code null}.
216         *
217         * @return The member value.
218         *
219         * @throws ParseException If the value is missing, {@code null} or not
220         *                        of the expected type.
221         */
222        public static double getDouble(final JSONObject o, final String key)
223                        throws ParseException {
224
225                return getGeneric(o, key, Number.class).doubleValue();
226        }
227
228
229        /**
230         * Gets a string member of a JSON object.
231         *
232         * @param o   The JSON object. Must not be {@code null}.
233         * @param key The JSON object member key. Must not be {@code null}.
234         *
235         * @return The member value.
236         *
237         * @throws ParseException If the value is missing, {@code null} or not
238         *                        of the expected type.
239         */
240        public static String getString(final JSONObject o, final String key)
241                        throws ParseException {
242
243                return getGeneric(o, key, String.class);
244        }
245
246
247        /**
248         * Gets a string member of a JSON object as {@code java.net.URI}.
249         *
250         * @param o   The JSON object. Must not be {@code null}.
251         * @param key The JSON object member key. Must not be {@code null}.
252         *
253         * @return The member value.
254         *
255         * @throws ParseException If the value is missing, {@code null} or not
256         *                        of the expected type.
257         */
258        public static URI getURI(final JSONObject o, final String key)
259                        throws ParseException {
260
261                try {
262                        return new URI(getGeneric(o, key, String.class));
263
264                } catch (URISyntaxException e) {
265
266                        throw new ParseException(e.getMessage(), 0);
267                }
268        }
269
270
271        /**
272         * Gets a JSON array member of a JSON object.
273         *
274         * @param o   The JSON object. Must not be {@code null}.
275         * @param key The JSON object member key. Must not be {@code null}.
276         *
277         * @return The member value.
278         *
279         * @throws ParseException If the value is missing, {@code null} or not
280         *                        of the expected type.
281         */
282        public static JSONArray getJSONArray(final JSONObject o, final String key)
283                        throws ParseException {
284
285                return getGeneric(o, key, JSONArray.class);
286        }
287
288
289        /**
290         * Gets a string array member of a JSON object.
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 String[] getStringArray(final JSONObject o, final String key)
301                        throws ParseException {
302
303                JSONArray jsonArray = getJSONArray(o, key);
304
305                try {
306                        return jsonArray.toArray(new String[0]);
307
308                } catch (ArrayStoreException e) {
309
310                        throw new ParseException("JSON object member with key \"" + key + "\" is not an array of strings", 0);
311                }
312        }
313
314        /**
315         * Gets a string list member of a JSON object
316         * 
317         * @param o   The JSON object. Must not be {@code null}.
318         * @param key The JSON object member key. Must not be {@code null}.
319         *
320         * @return The member value.
321         *
322         * @throws ParseException If the value is missing, {@code null} or not
323         *                        of the expected type.
324         */
325        public static List<String> getStringList(final JSONObject o, final String key) throws ParseException {
326
327                String[] array = getStringArray(o, key);
328
329                return Arrays.asList(array);
330
331        }
332
333        /**
334         * Gets a JSON object member of a JSON object.
335         *
336         * @param o   The JSON object. Must not be {@code null}.
337         * @param key The JSON object member key. Must not be {@code null}.
338         *
339         * @return The member value.
340         *
341         * @throws ParseException If the value is missing, {@code null} or not
342         *                        of the expected type.
343         */
344        public static JSONObject getJSONObject(final JSONObject o, final String key)
345                        throws ParseException {
346
347                return getGeneric(o, key, JSONObject.class);
348        }
349
350
351        /**
352         * Prevents public instantiation.
353         */
354        private JSONObjectUtils() {
355
356                // Nothing to do
357        }
358}
359