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