001package com.nimbusds.jose.util;
002
003
004import java.net.MalformedURLException;
005import java.net.URL;
006import java.text.ParseException;
007import java.util.Arrays;
008import java.util.List;
009
010import net.minidev.json.JSONArray;
011import net.minidev.json.JSONObject;
012import net.minidev.json.parser.JSONParser;
013
014
015/**
016 * JSON object helper methods for parsing and typed retrieval of member values.
017 *
018 * @author Vladimir Dzhuvinov
019 * @version $version$ (2013-01-08)
020 */
021public class JSONObjectUtils {
022
023
024        /**
025         * Parses a JSON object.
026         *
027         * <p>Specific JSON to Java entity mapping (as per JSON Smart):
028         *
029         * <ul>
030         *     <li>JSON true|false map to {@code java.lang.Boolean}.
031         *     <li>JSON numbers map to {@code java.lang.Number}.
032         *         <ul>
033         *             <li>JSON integer numbers map to {@code long}.
034         *             <li>JSON fraction numbers map to {@code double}.
035         *         </ul>
036         *     <li>JSON strings map to {@code java.lang.String}.
037         *     <li>JSON arrays map to {@code net.minidev.json.JSONArray}.
038         *     <li>JSON objects map to {@code net.minidev.json.JSONObject}.
039         * </ul>
040         *
041         * @param s The JSON object string to parse. Must not be {@code null}.
042         *
043         * @return The JSON object.
044         *
045         * @throws ParseException If the string cannot be parsed to a valid JSON 
046         *                        object.
047         */
048        public static JSONObject parseJSONObject(final String s) 
049                        throws ParseException {
050
051                Object o = null;
052
053                try {
054                        o = new JSONParser(JSONParser.USE_HI_PRECISION_FLOAT).parse(s);
055
056                } catch (net.minidev.json.parser.ParseException e) {
057
058                        throw new ParseException("Invalid JSON: " + e.getMessage(), 0);
059                }
060
061                if (o instanceof JSONObject) {
062                        return (JSONObject)o;
063                } else {
064                        throw new ParseException("JSON entity is not an object", 0);
065                }
066        }
067
068
069        /**
070         * Gets a generic member of a JSON object.
071         *
072         * @param o     The JSON object. Must not be {@code null}.
073         * @param key   The JSON object member key. Must not be {@code null}.
074         * @param clazz The expected class of the JSON object member value. Must
075         *              not be {@code null}.
076         *
077         * @return The JSON object member value.
078         *
079         * @throws ParseException If the value is missing, {@code null} or not
080         *                        of the expected type.
081         */
082        @SuppressWarnings("unchecked")
083        private static <T> T getGeneric(final JSONObject o, final String key, final Class<T> clazz)
084                        throws ParseException {
085
086                if (! o.containsKey(key)) {
087                        throw new ParseException("Missing JSON object member with key \"" + key + "\"", 0);
088                }
089
090                if (o.get(key) == null) {
091                        throw new ParseException("JSON object member with key \"" + key + "\" has null value", 0);
092                }
093
094                Object value = o.get(key);
095
096                if (! clazz.isAssignableFrom(value.getClass())) {
097                        throw new ParseException("Unexpected type of JSON object member with key \"" + key + "\"", 0);
098                }
099
100                return (T)value;
101        }
102
103
104        /**
105         * Gets a boolean 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         *
110         * @return The member value.
111         *
112         * @throws ParseException If the value is missing, {@code null} or not
113         *                        of the expected type.
114         */
115        public static boolean getBoolean(final JSONObject o, final String key)
116                        throws ParseException {
117
118                return getGeneric(o, key, Boolean.class);
119        }
120
121
122        /**
123         * Gets an number member of a JSON object as {@code int}.
124         *
125         * @param o   The JSON object. Must not be {@code null}.
126         * @param key The JSON object member key. Must not be {@code null}.
127         *
128         * @return The member value.
129         *
130         * @throws ParseException If the value is missing, {@code null} or not
131         *                        of the expected type.
132         */
133        public static int getInt(final JSONObject o, final String key)
134                        throws ParseException {
135
136                return getGeneric(o, key, Number.class).intValue();     
137        }
138
139
140        /**
141         * Gets a number member of a JSON object as {@code long}.
142         *
143         * @param o   The JSON object. Must not be {@code null}.
144         * @param key The JSON object member key. Must not be {@code null}.
145         *
146         * @return The member value.
147         *
148         * @throws ParseException If the value is missing, {@code null} or not
149         *                        of the expected type.
150         */
151        public static long getLong(final JSONObject o, final String key)
152                        throws ParseException {
153
154                return getGeneric(o, key, Number.class).longValue();
155        }
156
157
158        /**
159         * Gets a number member of a JSON object {@code float}.
160         *
161         * @param o   The JSON object. Must not be {@code null}.
162         * @param key The JSON object member key. Must not be {@code null}.
163         *
164         * @return The member value.
165         *
166         * @throws ParseException If the value is missing, {@code null} or not
167         *                        of the expected type.
168         */
169        public static float getFloat(final JSONObject o, final String key)
170                        throws ParseException {
171
172                return getGeneric(o, key, Number.class).floatValue();
173        }
174
175
176        /**
177         * Gets a number member of a JSON object as {@code double}.
178         *
179         * @param o   The JSON object. Must not be {@code null}.
180         * @param key The JSON object member key. Must not be {@code null}.
181         *
182         * @return The member value.
183         *
184         * @throws ParseException If the value is missing, {@code null} or not
185         *                        of the expected type.
186         */
187        public static double getDouble(final JSONObject o, final String key)
188                        throws ParseException {
189
190                return getGeneric(o, key, Number.class).doubleValue();
191        }
192
193
194        /**
195         * Gets a string member of a JSON object.
196         *
197         * @param o   The JSON object. Must not be {@code null}.
198         * @param key The JSON object member key. Must not be {@code null}.
199         *
200         * @return The member value.
201         *
202         * @throws ParseException If the value is missing, {@code null} or not
203         *                        of the expected type.
204         */
205        public static String getString(final JSONObject o, final String key)
206                        throws ParseException {
207
208                return getGeneric(o, key, String.class);
209        }
210
211
212        /**
213         * Gets a string member of a JSON object as {@code java.net.URL}.
214         *
215         * @param o   The JSON object. Must not be {@code null}.
216         * @param key The JSON object member key. Must not be {@code null}.
217         *
218         * @return The member value.
219         *
220         * @throws ParseException If the value is missing, {@code null} or not
221         *                        of the expected type.
222         */
223        public static URL getURL(final JSONObject o, final String key)
224                        throws ParseException {
225
226                try {
227                        return new URL(getGeneric(o, key, String.class));
228
229                } catch (MalformedURLException e) {
230
231                        throw new ParseException(e.getMessage(), 0);
232                }
233        }
234
235
236        /**
237         * Gets a JSON array member of a JSON object.
238         *
239         * @param o   The JSON object. Must not be {@code null}.
240         * @param key The JSON object member key. Must not be {@code null}.
241         *
242         * @return The member value.
243         *
244         * @throws ParseException If the value is missing, {@code null} or not
245         *                        of the expected type.
246         */
247        public static JSONArray getJSONArray(final JSONObject o, final String key)
248                        throws ParseException {
249
250                return getGeneric(o, key, JSONArray.class);
251        }
252
253
254        /**
255         * Gets a string array member of a JSON object.
256         *
257         * @param o   The JSON object. Must not be {@code null}.
258         * @param key The JSON object member key. Must not be {@code null}.
259         *
260         * @return The member value.
261         *
262         * @throws ParseException If the value is missing, {@code null} or not
263         *                        of the expected type.
264         */
265        public static String[] getStringArray(final JSONObject o, final String key)
266                        throws ParseException {
267
268                JSONArray jsonArray = getJSONArray(o, key);
269
270                try {
271                        return jsonArray.toArray(new String[0]);
272
273                } catch (ArrayStoreException e) {
274
275                        throw new ParseException("JSON object member with key \"" + key + "\" is not an array of strings", 0);
276                }
277        }
278
279        /**
280         * Gets a string list member of a JSON object
281         * 
282         * @param o   The JSON object. Must not be {@code null}.
283         * @param key The JSON object member key. Must not be {@code null}.
284         *
285         * @return The member value.
286         *
287         * @throws ParseException If the value is missing, {@code null} or not
288         *                        of the expected type.
289         */
290        public static List<String> getStringList(final JSONObject o, final String key) throws ParseException {
291
292                String[] array = getStringArray(o, key);
293
294                return Arrays.asList(array);
295
296        }
297
298        /**
299         * Gets a JSON object member of a JSON object.
300         *
301         * @param o   The JSON object. Must not be {@code null}.
302         * @param key The JSON object member key. Must not be {@code null}.
303         *
304         * @return The member value.
305         *
306         * @throws ParseException If the value is missing, {@code null} or not
307         *                        of the expected type.
308         */
309        public static JSONObject getJSONObject(final JSONObject o, final String key)
310                        throws ParseException {
311
312                return getGeneric(o, key, JSONObject.class);
313        }
314
315
316        /**
317         * Prevents public instantiation.
318         */
319        private JSONObjectUtils() {
320
321                // Nothing to do
322        }
323}
324