001    package com.nimbusds.jose.util;
002    
003    
004    import java.net.MalformedURLException;
005    import java.net.URL;
006    import java.text.ParseException;
007    import java.util.Arrays;
008    import java.util.List;
009    
010    import net.minidev.json.JSONArray;
011    import net.minidev.json.JSONObject;
012    import 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     */
021    public 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