3 import java.util.HashMap;
4 import java.util.Hashtable;
9 * a very simple JSON parser for JSON objects that are compatible with JavaScript
10 * A gross simplification of https://github.com/douglascrockford/JSON-java
12 * A SUBSET of JSON with similarly to window.JSON.parse():
14 * In JavaScript returns "null" for a null value, not null
16 * -- requires quoted strings for keys and values
18 * -- does not allow /xxx/ objects
23 public class JSJSONParser {
28 private boolean asHashTable;
30 public JSJSONParser () {
35 * requires { "key":"value", "key":"value",....}
38 * @param asHashTable TODO
42 @SuppressWarnings("unchecked")
43 public Map<String, Object> parseMap(String str, boolean asHashTable) {
45 this.asHashTable = asHashTable;
51 return (Map<String, Object>) getValue(false);
55 * Could return Integer, Float, Boolean, String, Map<String, Object>, Lst<Object>, or null
59 * @return a object equivalent to the JSON string str
62 public Object parse(String str, boolean asHashTable) {
64 this.asHashTable = asHashTable;
67 return getValue(false);
71 return (index < len ? str.charAt(index++) : '\0');
74 private void returnChar() {
79 * Get the next char in the string, skipping whitespace.
81 * @throws JSONException
82 * @return one character, or 0 if there are no more characters.
84 private char getChar() throws JSONException {
87 if (c == 0 || c > ' ') {
94 * only allowing the following values:
107 * @param isKey if we should allow {...} and [...]
108 * @return a subclass of Object
109 * @throws JSONException
111 private Object getValue(boolean isKey) throws JSONException {
131 // standard syntax is assumed; not checking all possible invalid keys
132 // for example, "-" is not allowed in JavaScript, which is what this is for
134 while (c >= ' ' && "[,]{:}'\"".indexOf(c) < 0)
137 if (isKey && c != ':')
142 throw new JSONException("invalid key");
144 String string = str.substring(i, index).trim();
146 // check for the only valid simple words: true, false, null (lower case)
147 // and in this case, only for
150 if (string.equals("true")) {
153 if (string.equals("false")) {
154 return Boolean.FALSE;
156 if (string.equals("null")) {
157 return (asHashTable ? string : null);
160 // only numbers from here on:
161 c = string.charAt(0);
162 if (c >= '0' && c <= '9' || c == '-')
164 if (string.indexOf('.') < 0 && string.indexOf('e') < 0
165 && string.indexOf('E') < 0)
166 return new Integer(string);
167 // not allowing infinity or NaN
168 // using float here because Jmol does not use Double
169 Float d = Float.valueOf(string);
170 if (!d.isInfinite() && !d.isNaN())
172 } catch (Exception e) {
174 // not a valid number
175 System.out.println("JSON parser cannot parse " + string);
176 throw new JSONException("invalid value");
179 private String getString(char quote) throws JSONException {
185 switch (c = next()) {
189 throw syntaxError("Unterminated string");
191 switch (c = next()) {
216 c = (char) Integer.parseInt(str.substring(i, index), 16);
217 } catch (Exception e) {
218 throw syntaxError("Substring bounds error");
222 throw syntaxError("Illegal escape.");
227 return (sb == null ? str.substring(i0, i1) : sb.toString());
230 if (index > i1 + 1) {
233 sb.append(str.substring(i0, i1));
241 private Object getObject() {
242 Map<String, Object> map = (asHashTable ? new Hashtable<String, Object>() : new HashMap<String, Object>());
248 throw new JSONException("invalid object");
251 boolean isKey = false;
253 if ((isKey = !isKey) == true)
254 key = getValue(true).toString();
256 map.put(key, getValue(false));
270 throw syntaxError("Expected ',' or ':' or '}'");
275 private Object getArray() {
276 Lst<Object> l = new Lst<Object>();
281 throw new JSONException("invalid array");
284 boolean isNull = false;
290 l.addLast(getValue(false));
309 throw syntaxError("Expected ',' or ']'");
315 * Make a JSONException to signal a syntax error.
319 * @return A JSONException object, suitable for throwing
321 public JSONException syntaxError(String message) {
322 return new JSONException(message + " for " + str.substring(0, Math.min(index, len)));