├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md └── src └── cc └── nnproject └── json ├── AbstractJSON.java ├── BufferedReader.java ├── JSON.java ├── JSONArray.java ├── JSONException.java ├── JSONObject.java └── JSONStream.java /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.jar 2 | *.zip 3 | # Compiled classes folder 4 | bin 5 | # Eclipse settings 6 | .settings 7 | 8 | .project 9 | .classpath -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Arman Jussupgaliyev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cc.nnproject.json 2 | JSON library for Java, compatible with CLDC 1.1 & JDK 1.1 3 | 4 | ## Usage example: 5 | 6 | `JSON`: 7 | ``` 8 | JSONObject json = JSON.getObject(str); 9 | System.out.println(json.getArray("messages").getObject(0).getNullableString("text")); 10 | ``` 11 | 12 | `JSONArray`, `JSONObject`: 13 | ``` 14 | JSONArray objects = new JSONArray(); 15 | 16 | JSONObject object1 = new JSONObject(); 17 | object1.put("some", "Example text"); 18 | objects.add(object1); 19 | 20 | JSONObject object2 = new JSONObject(); 21 | object2.put("n", 292); 22 | objects.add(object2); 23 | 24 | System.out.println(objects.build()); 25 | ``` 26 | 27 | `JSONStream`: 28 | ``` 29 | JSONStream stream = JSONStream.getStream(connection.openInputStream()); 30 | try { 31 | stream.expectNextTrim('{'); 32 | 33 | if(!stream.jumpToKey("response")) return; 34 | stream.expectNextTrim('{'); 35 | 36 | if(!stream.jumpToKey("items")) return; 37 | stream.expectNextTrim('['); 38 | 39 | if(!stream.skipArrayElements(3)) return; 40 | stream.expectNextTrim('{'); 41 | 42 | if(!stream.jumpToKey("date")) return; 43 | 44 | System.out.println(stream.nextValue()); 45 | } finally { 46 | stream.close(); 47 | } 48 | ``` 49 | -------------------------------------------------------------------------------- /src/cc/nnproject/json/AbstractJSON.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2022-2025 Arman Jussupgaliyev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | package cc.nnproject.json; 23 | 24 | import java.io.IOException; 25 | import java.io.OutputStream; 26 | 27 | public abstract class AbstractJSON { 28 | 29 | // common methods for both JSONObject and JSONArray 30 | 31 | public abstract void clear(); 32 | 33 | public abstract int size(); 34 | 35 | public abstract String toString(); 36 | 37 | public abstract String build(); 38 | 39 | public final String format() { 40 | return format(0); 41 | } 42 | 43 | public abstract void write(OutputStream out) throws IOException; 44 | 45 | protected abstract String format(int l); 46 | 47 | public abstract boolean similar(Object obj); 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/cc/nnproject/json/BufferedReader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). 3 | * All rights reserved. 4 | * This component and the accompanying materials are made available 5 | * under the terms of "Eclipse Public License v1.0" 6 | * which accompanies this distribution, and is available 7 | * at the URL "http://www.eclipse.org/legal/epl-v10.html". 8 | * 9 | * Initial Contributors: 10 | * Nokia Corporation - initial contribution. 11 | * 12 | * Contributors: 13 | * 14 | * Description: 15 | * 16 | */ 17 | 18 | 19 | package cc.nnproject.json; 20 | 21 | import java.io.IOException; 22 | import java.io.Reader; 23 | 24 | /** 25 | * Buffered wrapper for Readers. 26 | * 27 | * @see java.io.BufferedReader 28 | */ 29 | public class BufferedReader extends Reader 30 | { 31 | /** Default buffer size. */ 32 | private static final int BUF_SIZE = 16384; 33 | 34 | /** Reader given in the constructor. */ 35 | private Reader iReader = null; 36 | 37 | /** Character buffer. */ 38 | private char[] iBuf = null; 39 | 40 | /** Amount of characters in the buffer. 41 | Value must be between zero and iBuf.length. */ 42 | private int iBufAmount = 0; 43 | 44 | /** Current read position in the buffer. 45 | Value must be between zero and iBuf.length. */ 46 | private int iBufPos = 0; 47 | 48 | /** 49 | * @see java.io.BufferedReader#Constructor(java.io.Reader) 50 | */ 51 | public BufferedReader(Reader aIn) 52 | { 53 | this(aIn, BUF_SIZE); 54 | } 55 | 56 | /** 57 | * @see java.io.BufferedReader#Constructor(java.io.Reader, int) 58 | */ 59 | public BufferedReader(Reader aIn, int aSize) 60 | { 61 | if (aSize <= 0) 62 | { 63 | throw new IllegalArgumentException( 64 | "BufferedReader: Invalid buffer size"); 65 | } 66 | iBuf = new char[aSize]; 67 | iReader = aIn; 68 | } 69 | 70 | /** 71 | * @see java.io.BufferedReader#close() 72 | */ 73 | public void close() throws IOException 74 | { 75 | iBuf = null; 76 | iBufAmount = 0; 77 | iBufPos = 0; 78 | if (iReader != null) 79 | { 80 | iReader.close(); 81 | } 82 | } 83 | 84 | /** 85 | * @see java.io.BufferedReader#read() 86 | */ 87 | public int read() throws IOException 88 | { 89 | int result = 0; 90 | if (iBufPos >= iBufAmount) 91 | { 92 | result = fillBuf(); 93 | } 94 | if (result > -1) 95 | { 96 | result = iBuf[iBufPos++]; 97 | } 98 | return result; 99 | } 100 | 101 | /** 102 | * @see java.io.BufferedReader#read(char[]) 103 | */ 104 | public int read(char[] aBuf) throws IOException 105 | { 106 | return read(aBuf, 0, aBuf.length); 107 | } 108 | 109 | /** 110 | * @see java.io.BufferedReader#read(char[], int, int) 111 | */ 112 | public int read(char[] aBuf, int aOffset, int aLength) throws IOException 113 | { 114 | if (aOffset < 0 || aOffset >= aBuf.length) 115 | { 116 | throw new IllegalArgumentException( 117 | "BufferedReader: Invalid buffer offset"); 118 | } 119 | int charsToRead = aBuf.length - aOffset; 120 | if (charsToRead > aLength) 121 | { 122 | charsToRead = aLength; 123 | } 124 | int bufCharCount = iBufAmount - iBufPos; 125 | int readCount = 0; 126 | if (charsToRead <= bufCharCount) 127 | { 128 | // All characters can be read from the buffer. 129 | for (int i = 0; i < charsToRead; i++) 130 | { 131 | aBuf[aOffset+i] = iBuf[iBufPos++]; 132 | } 133 | readCount += charsToRead; 134 | } 135 | else 136 | { 137 | // First read characters from the buffer, 138 | // then read more characters from the Reader. 139 | for (int i = 0; i < bufCharCount; i++) 140 | { 141 | aBuf[aOffset+i] = iBuf[iBufPos++]; 142 | } 143 | readCount += bufCharCount; 144 | // Whole buffer has now been read, fill the buffer again. 145 | if (fillBuf() > -1) 146 | { 147 | // Read the remaining characters. 148 | readCount += read(aBuf, aOffset+readCount, aLength-readCount); 149 | } 150 | } 151 | if (readCount <= 0) 152 | { 153 | // Nothing has been read, return -1 to indicate end of stream. 154 | readCount = -1; 155 | } 156 | return readCount; 157 | } 158 | 159 | /** 160 | * @see java.io.BufferedReader#readLine() 161 | */ 162 | public String readLine() throws IOException 163 | { 164 | if (!ensureBuf()) 165 | { 166 | // End of stream has been reached. 167 | return null; 168 | } 169 | StringBuffer line = new StringBuffer(); 170 | while (ensureBuf()) 171 | { 172 | if (skipEol()) 173 | { 174 | // End of line found. 175 | break; 176 | } 177 | else 178 | { 179 | // Append characters to result line. 180 | line.append(iBuf[iBufPos++]); 181 | } 182 | } 183 | return line.toString(); 184 | } 185 | 186 | /** 187 | * @see java.io.BufferedReader#ready() 188 | */ 189 | public boolean ready() throws IOException 190 | { 191 | if (iBufPos < iBufAmount) 192 | { 193 | return true; 194 | } 195 | if (iReader != null) 196 | { 197 | return iReader.ready(); 198 | } 199 | return false; 200 | } 201 | 202 | /** 203 | * @see java.io.BufferedReader#skip() 204 | */ 205 | public long skip(long aAmountToSkip) throws IOException 206 | { 207 | if (aAmountToSkip < 0) 208 | { 209 | throw new IllegalArgumentException( 210 | "BufferedReader: Cannot skip negative amount of characters"); 211 | } 212 | long skipped = 0; 213 | int bufCharCount = iBufAmount - iBufPos; 214 | if (aAmountToSkip <= bufCharCount) 215 | { 216 | // There is enough characters in buffer to skip. 217 | iBufPos += aAmountToSkip; 218 | skipped += aAmountToSkip; 219 | } 220 | else 221 | { 222 | // First skip characters that are available in the buffer, 223 | // then skip characters from the Reader. 224 | iBufPos += bufCharCount; 225 | skipped += bufCharCount; 226 | if (iReader != null) 227 | { 228 | skipped += iReader.skip(aAmountToSkip - skipped); 229 | } 230 | } 231 | return skipped; 232 | } 233 | 234 | /** 235 | * If current read position in the buffer is end of line, 236 | * move position over end of line and return true, otherwise 237 | * return false. Also in the end of stream case this method 238 | * returns true. 239 | */ 240 | private boolean skipEol() throws IOException 241 | { 242 | if (!ensureBuf()) 243 | { 244 | // End of stream has been reached. 245 | return true; 246 | } 247 | boolean eolFound = false; 248 | if (iBufAmount > iBufPos && iBuf[iBufPos] == '\r') 249 | { 250 | iBufPos += 1; 251 | eolFound = true; 252 | ensureBuf(); 253 | } 254 | if (iBufAmount > iBufPos && iBuf[iBufPos] == '\n') 255 | { 256 | iBufPos += 1; 257 | eolFound = true; 258 | } 259 | return eolFound; 260 | } 261 | 262 | /** 263 | * Ensures that the buffer has characters to read. 264 | * 265 | * @return True if the buffer has characters to read, 266 | * false if end of stream has been reached. 267 | */ 268 | private boolean ensureBuf() throws IOException 269 | { 270 | boolean result = true; 271 | if (iBufPos >= iBufAmount) 272 | { 273 | if (fillBuf() == -1) 274 | { 275 | result = false; 276 | } 277 | } 278 | return result; 279 | } 280 | 281 | /** 282 | * Fills the buffer from the Reader and resets the buffer counters. 283 | * 284 | * @return The number of characters read, or -1 if the end of 285 | * stream has been reached. 286 | */ 287 | private int fillBuf() throws IOException 288 | { 289 | if (iReader == null) 290 | { 291 | return -1; 292 | } 293 | // Fill the buffer. 294 | int readCount = iReader.read(iBuf); 295 | if (readCount > -1) 296 | { 297 | // Reset the buffer counters only if reading succeeded. 298 | iBufAmount = readCount; 299 | iBufPos = 0; 300 | } 301 | return readCount; 302 | } 303 | } 304 | -------------------------------------------------------------------------------- /src/cc/nnproject/json/JSON.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2021-2025 Arman Jussupgaliyev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | package cc.nnproject.json; 23 | 24 | import java.io.IOException; 25 | import java.io.OutputStream; 26 | import java.util.Hashtable; 27 | import java.util.Vector; 28 | 29 | /** 30 | * JSON Library compatible with CLDC 1.1 & JDK 1.1
31 | * Usage:

JSONObject obj = JSON.getObject(str);

32 | * When using proguard, add:

-optimizations !code/simplification/object 33 | * @author Shinovon 34 | * @version 2.4 35 | */ 36 | public final class JSON { 37 | 38 | // parse all nested elements once 39 | static final boolean parse_members = false; 40 | 41 | // identation for formatting 42 | static final String FORMAT_TAB = " "; 43 | 44 | // used internally for storing nulls, get methods must return real null 45 | public static final Object json_null = new Object(); 46 | 47 | public static final Boolean TRUE = new Boolean(true); 48 | public static final Boolean FALSE = new Boolean(false); 49 | 50 | public static AbstractJSON get(String text) throws JSONException { 51 | if (text == null || text.length() <= 1) 52 | throw new JSONException("Empty text"); 53 | char c = text.charAt(0); 54 | if (c != '{' && c != '[') 55 | throw new JSONException("Not JSON object or array"); 56 | return (AbstractJSON) parseJSON(text.trim()); 57 | } 58 | 59 | public static JSONObject getObject(String text) throws JSONException { 60 | if (text == null || text.length() <= 1) 61 | throw new JSONException("Empty text"); 62 | if (text.charAt(0) != '{') 63 | throw new JSONException("Not JSON object"); 64 | return (JSONObject) parseJSON(text.trim()); 65 | } 66 | 67 | public static JSONArray getArray(String text) throws JSONException { 68 | if (text == null || text.length() <= 1) 69 | throw new JSONException("Empty text"); 70 | if (text.charAt(0) != '[') 71 | throw new JSONException("Not JSON array"); 72 | return (JSONArray) parseJSON(text.trim()); 73 | } 74 | 75 | static Object getJSON(Object obj) throws JSONException { 76 | if (obj instanceof Hashtable) { 77 | return new JSONObject((Hashtable) obj); 78 | } 79 | if (obj instanceof Vector) { 80 | return new JSONArray((Vector) obj); 81 | } 82 | if (obj == null) { 83 | return json_null; 84 | } 85 | return obj; 86 | } 87 | 88 | static Object parseJSON(String str) throws JSONException { 89 | char first = str.charAt(0); 90 | int length; 91 | char last = str.charAt(length = str.length() - 1); 92 | if (last <= ' ') 93 | last = (str = str.trim()).charAt(length = str.length() - 1); 94 | switch (first) { 95 | case '"': { // string 96 | if (last != '"') 97 | throw new JSONException("Unexpected end of text"); 98 | if (str.indexOf('\\') != -1) { 99 | char[] chars = str.substring(1, length).toCharArray(); 100 | str = null; 101 | int l = chars.length; 102 | StringBuffer sb = new StringBuffer(); 103 | int i = 0; 104 | // parse escaped chars in string 105 | loop: { 106 | while (i < l) { 107 | char c = chars[i]; 108 | switch (c) { 109 | case '\\': { 110 | next: { 111 | replace: { 112 | if (l < i + 1) { 113 | sb.append(c); 114 | break loop; 115 | } 116 | char c1 = chars[i + 1]; 117 | switch (c1) { 118 | case 'u': 119 | i+=2; 120 | sb.append((char) Integer.parseInt( 121 | new String(new char[] {chars[i++], chars[i++], chars[i++], chars[i++]}), 122 | 16)); 123 | break replace; 124 | case 'x': 125 | i+=2; 126 | sb.append((char) Integer.parseInt( 127 | new String(new char[] {chars[i++], chars[i++]}), 128 | 16)); 129 | break replace; 130 | case 'n': 131 | sb.append('\n'); 132 | i+=2; 133 | break replace; 134 | case 'r': 135 | sb.append('\r'); 136 | i+=2; 137 | break replace; 138 | case 't': 139 | sb.append('\t'); 140 | i+=2; 141 | break replace; 142 | case 'f': 143 | sb.append('\f'); 144 | i+=2; 145 | break replace; 146 | case 'b': 147 | sb.append('\b'); 148 | i+=2; 149 | break replace; 150 | case '\"': 151 | case '\'': 152 | case '\\': 153 | case '/': 154 | i+=2; 155 | sb.append((char) c1); 156 | break replace; 157 | default: 158 | break next; 159 | } 160 | } 161 | break; 162 | } 163 | sb.append(c); 164 | i++; 165 | break; 166 | } 167 | default: 168 | sb.append(c); 169 | i++; 170 | } 171 | } 172 | } 173 | str = sb.toString(); 174 | sb = null; 175 | return str; 176 | } 177 | return str.substring(1, length); 178 | } 179 | case '{': // JSON object or array 180 | case '[': { 181 | boolean object = first == '{'; 182 | if (object ? last != '}' : last != ']') 183 | throw new JSONException("Unexpected end of text"); 184 | int brackets = 0; 185 | int i = 1; 186 | char nextDelimiter = object ? ':' : ','; 187 | boolean escape = false; 188 | String key = null; 189 | Object res = object ? (Object) new JSONObject() : (Object) new JSONArray(); 190 | 191 | for (int splIndex; i < length; i = splIndex + 1) { 192 | // skip all spaces 193 | for (; i < length - 1 && str.charAt(i) <= ' '; i++); 194 | 195 | splIndex = i; 196 | boolean quote = false; 197 | for (; splIndex < length && (quote || brackets > 0 || str.charAt(splIndex) != nextDelimiter); splIndex++) { 198 | char c = str.charAt(splIndex); 199 | if (!escape) { 200 | if (c == '\\') { 201 | escape = true; 202 | } else if (c == '"') { 203 | quote = !quote; 204 | } 205 | } else escape = false; 206 | 207 | if (!quote) { 208 | if (c == '{' || c == '[') { 209 | brackets++; 210 | } else if (c == '}' || c == ']') { 211 | brackets--; 212 | } 213 | } 214 | } 215 | 216 | // fail if unclosed quotes or brackets left 217 | if (quote || brackets > 0) { 218 | throw new JSONException("Corrupted JSON"); 219 | } 220 | 221 | if (object && key == null) { 222 | key = str.substring(i, splIndex); 223 | key = key.substring(1, key.length() - 1); 224 | nextDelimiter = ','; 225 | } else { 226 | Object value = str.substring(i, splIndex).trim(); 227 | // don't check length because if value is empty, then exception is going to be thrown anyway 228 | char c = ((String) value).charAt(0); 229 | // leave JSONString as value to parse it later, if its object or array and nested parsing is disabled 230 | value = parse_members || (c != '{' && c != '[') ? 231 | parseJSON((String) value) : new String[] {(String) value}; 232 | if (object) { 233 | ((JSONObject) res)._put(key, value); 234 | key = null; 235 | nextDelimiter = ':'; 236 | } else if (splIndex > i) { 237 | ((JSONArray) res).addElement(value); 238 | } 239 | } 240 | } 241 | return res; 242 | } 243 | case 'n': // null 244 | return json_null; 245 | case 't': // true 246 | return TRUE; 247 | case 'f': // false 248 | return FALSE; 249 | default: // number 250 | if ((first >= '0' && first <= '9') || first == '-') { 251 | try { 252 | // hex 253 | if (length > 1 && first == '0' && str.charAt(1) == 'x') { 254 | if (length > 9) // str.length() > 10 255 | return new Long(Long.parseLong(str.substring(2), 16)); 256 | return new Integer(Integer.parseInt(str.substring(2), 16)); 257 | } 258 | // decimal 259 | if (str.indexOf('.') != -1 || str.indexOf('E') != -1 || "-0".equals(str)) 260 | return new Double(Double.parseDouble(str)); 261 | if (first == '-') length--; 262 | if (length > 8) // (str.length() - (str.charAt(0) == '-' ? 1 : 0)) >= 10 263 | return new Long(Long.parseLong(str)); 264 | return new Integer(Integer.parseInt(str)); 265 | } catch (Exception e) {} 266 | } 267 | throw new JSONException("Couldn't be parsed: " + str); 268 | // return new String[](str); 269 | } 270 | } 271 | 272 | public static boolean isNull(Object obj) { 273 | return obj == json_null || obj == null; 274 | } 275 | 276 | // transforms string for exporting 277 | static String escape_utf8(String s) { 278 | int len = s.length(); 279 | StringBuffer sb = new StringBuffer(); 280 | int i = 0; 281 | while (i < len) { 282 | char c = s.charAt(i); 283 | switch (c) { 284 | case '"': 285 | case '\\': 286 | sb.append("\\").append(c); 287 | break; 288 | case '\b': 289 | sb.append("\\b"); 290 | break; 291 | case '\f': 292 | sb.append("\\f"); 293 | break; 294 | case '\n': 295 | sb.append("\\n"); 296 | break; 297 | case '\r': 298 | sb.append("\\r"); 299 | break; 300 | case '\t': 301 | sb.append("\\t"); 302 | break; 303 | default: 304 | if (c < 32 || c > 1103 || (c >= '\u0080' && c < '\u00a0')) { 305 | String u = Integer.toHexString(c); 306 | sb.append("\\u"); 307 | for (int z = u.length(); z < 4; z++) { 308 | sb.append('0'); 309 | } 310 | sb.append(u); 311 | } else { 312 | sb.append(c); 313 | } 314 | } 315 | i++; 316 | } 317 | return sb.toString(); 318 | } 319 | 320 | static double getDouble(Object o) throws JSONException { 321 | try { 322 | if (o instanceof String[]) 323 | return Double.parseDouble(((String[]) o)[0]); 324 | if (o instanceof Integer) 325 | return ((Integer) o).intValue(); 326 | if (o instanceof Long) 327 | return ((Long) o).longValue(); 328 | if (o instanceof Double) 329 | return ((Double) o).doubleValue(); 330 | } catch (Throwable e) {} 331 | throw new JSONException("Cast to double failed: " + o); 332 | } 333 | 334 | static int getInt(Object o) throws JSONException { 335 | try { 336 | if (o instanceof String[]) 337 | return Integer.parseInt(((String[]) o)[0]); 338 | if (o instanceof Integer) 339 | return ((Integer) o).intValue(); 340 | if (o instanceof Long) 341 | return (int) ((Long) o).longValue(); 342 | if (o instanceof Double) 343 | return ((Double) o).intValue(); 344 | } catch (Throwable e) {} 345 | throw new JSONException("Cast to int failed: " + o); 346 | } 347 | 348 | static long getLong(Object o) throws JSONException { 349 | try { 350 | if (o instanceof String[]) 351 | return Long.parseLong(((String[]) o)[0]); 352 | if (o instanceof Integer) 353 | return ((Integer) o).longValue(); 354 | if (o instanceof Long) 355 | return ((Long) o).longValue(); 356 | if (o instanceof Double) 357 | return ((Double) o).longValue(); 358 | } catch (Throwable e) {} 359 | throw new JSONException("Cast to long failed: " + o); 360 | } 361 | 362 | public static void writeString(OutputStream out, String s) throws IOException { 363 | int len = s.length(); 364 | for (int i = 0; i < len; ++i) { 365 | char c = s.charAt(i); 366 | switch (c) { 367 | case '"': 368 | case '\\': 369 | out.write((byte) '\\'); 370 | out.write((byte) c); 371 | break; 372 | case '\b': 373 | out.write((byte) '\\'); 374 | out.write((byte) 'b'); 375 | break; 376 | case '\f': 377 | out.write((byte) '\\'); 378 | out.write((byte) 'f'); 379 | break; 380 | case '\n': 381 | out.write((byte) '\\'); 382 | out.write((byte) 'n'); 383 | break; 384 | case '\r': 385 | out.write((byte) '\\'); 386 | out.write((byte) 'r'); 387 | break; 388 | case '\t': 389 | out.write((byte) '\\'); 390 | out.write((byte) 't'); 391 | break; 392 | default: 393 | if (c < 32 || c > 255) { 394 | out.write((byte) '\\'); 395 | out.write((byte) 'u'); 396 | String u = Integer.toHexString(c); 397 | for (int z = u.length(); z < 4; z++) { 398 | out.write((byte) '0'); 399 | } 400 | out.write(u.getBytes()); 401 | } else { 402 | out.write((byte) c); 403 | } 404 | } 405 | } 406 | } 407 | 408 | } 409 | -------------------------------------------------------------------------------- /src/cc/nnproject/json/JSONArray.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2021-2025 Arman Jussupgaliyev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | package cc.nnproject.json; 23 | 24 | import java.io.IOException; 25 | import java.io.OutputStream; 26 | import java.util.Enumeration; 27 | import java.util.Vector; 28 | 29 | public class JSONArray extends AbstractJSON { 30 | 31 | protected Object[] elements; 32 | protected int count; 33 | 34 | public JSONArray() { 35 | elements = new Object[10]; 36 | } 37 | 38 | public JSONArray(int size) { 39 | elements = new Object[size]; 40 | } 41 | 42 | /** 43 | * @deprecated Doesn't adapt nested elements 44 | */ 45 | public JSONArray(Vector vector) { 46 | elements = new Object[count = vector.size()]; 47 | vector.copyInto(elements); 48 | } 49 | 50 | /** 51 | * @deprecated Compatibility with org.json 52 | */ 53 | public JSONArray(String str) { 54 | JSONArray tmp = JSON.getArray(str); // FIXME 55 | elements = tmp.elements; 56 | count = tmp.count; 57 | } 58 | 59 | public Object get(int index) throws JSONException { 60 | if (index < 0 || index >= count) { 61 | throw new JSONException("Index out of bounds: " + index); 62 | } 63 | try { 64 | Object o = elements[index]; 65 | if (o instanceof String[]) 66 | o = elements[index] = JSON.parseJSON(((String[]) o)[0]); 67 | if (o == JSON.json_null) 68 | return null; 69 | return o; 70 | } catch (Exception e) { 71 | } 72 | throw new JSONException("No value at " + index); 73 | } 74 | 75 | // unused methods should be removed by proguard shrinking 76 | 77 | public Object get(int index, Object def) { 78 | try { 79 | return get(index); 80 | } catch (Exception e) { 81 | return def; 82 | } 83 | } 84 | 85 | public Object getNullable(int index) { 86 | return get(index, null); 87 | } 88 | 89 | public String getString(int index) throws JSONException { 90 | Object o = get(index); 91 | if (o == null || o instanceof String) 92 | return (String) o; 93 | return String.valueOf(o); 94 | } 95 | 96 | public String getString(int index, String def) { 97 | try { 98 | Object o = get(index); 99 | if (o == null || o instanceof String) 100 | return (String) o; 101 | return String.valueOf(o); 102 | } catch (Exception e) { 103 | return def; 104 | } 105 | } 106 | 107 | public String getNullableString(int index) { 108 | return getString(index, null); 109 | } 110 | 111 | public JSONObject getObject(int index) throws JSONException { 112 | try { 113 | return (JSONObject) get(index); 114 | } catch (ClassCastException e) { 115 | throw new JSONException("Not object at " + index); 116 | } 117 | } 118 | 119 | public JSONObject getObject(int index, JSONObject def) { 120 | try { 121 | return getObject(index); 122 | } catch (Exception e) { 123 | } 124 | return def; 125 | } 126 | 127 | public JSONObject getNullableObject(int index) { 128 | return getObject(index, null); 129 | } 130 | 131 | public JSONArray getArray(int index) throws JSONException { 132 | try { 133 | return (JSONArray) get(index); 134 | } catch (ClassCastException e) { 135 | throw new JSONException("Not array at " + index); 136 | } 137 | } 138 | 139 | public JSONArray getArray(int index, JSONArray def) { 140 | try { 141 | return getArray(index); 142 | } catch (Exception e) { 143 | } 144 | return def; 145 | } 146 | 147 | public JSONArray getNullableArray(int index) { 148 | return getArray(index, null); 149 | } 150 | 151 | public int getInt(int index) throws JSONException { 152 | return JSON.getInt(get(index)); 153 | } 154 | 155 | public int getInt(int index, int def) { 156 | try { 157 | return getInt(index); 158 | } catch (Exception e) { 159 | return def; 160 | } 161 | } 162 | 163 | public long getLong(int index) throws JSONException { 164 | return JSON.getLong(get(index)); 165 | } 166 | 167 | public long getLong(int index, long def) { 168 | try { 169 | return getLong(index); 170 | } catch (Exception e) { 171 | return def; 172 | } 173 | } 174 | 175 | public double getDouble(int index) throws JSONException { 176 | return JSON.getDouble(get(index)); 177 | } 178 | 179 | public double getDouble(int index, double def) { 180 | try { 181 | return getDouble(index); 182 | } catch (Exception e) { 183 | return def; 184 | } 185 | } 186 | 187 | public boolean getBoolean(int index) throws JSONException { 188 | Object o = get(index); 189 | if (o == JSON.TRUE) return true; 190 | if (o == JSON.FALSE) return false; 191 | if (o instanceof Boolean) return ((Boolean) o).booleanValue(); 192 | if (o instanceof String) { 193 | String s = (String) o; 194 | s = s.toLowerCase(); 195 | if (s.equals("true")) return true; 196 | if (s.equals("false")) return false; 197 | } 198 | throw new JSONException("Not boolean: " + o + " (" + index + ")"); 199 | } 200 | 201 | public boolean getBoolean(int index, boolean def) { 202 | try { 203 | return getBoolean(index); 204 | } catch (Exception e) { 205 | return def; 206 | } 207 | } 208 | 209 | public boolean isNull(int index) { 210 | if (index < 0 || index >= count) { 211 | throw new JSONException("Index out of bounds: " + index); 212 | } 213 | return elements[index] == JSON.json_null; 214 | } 215 | 216 | /** 217 | * @deprecated 218 | */ 219 | public void add(Object object) { 220 | if (object == this) throw new JSONException(); 221 | addElement(JSON.getJSON(object)); 222 | } 223 | 224 | public void add(AbstractJSON json) { 225 | if (json == this) throw new JSONException(); 226 | addElement(json); 227 | } 228 | 229 | public void add(String s) { 230 | addElement(s); 231 | } 232 | 233 | public void add(int i) { 234 | addElement(new Integer(i)); 235 | } 236 | 237 | public void add(long l) { 238 | addElement(new Long(l)); 239 | } 240 | 241 | public void add(double d) { 242 | addElement(new Double(d)); 243 | } 244 | 245 | public void add(boolean b) { 246 | addElement(new Boolean(b)); 247 | } 248 | 249 | /** 250 | * @deprecated 251 | */ 252 | public void set(int index, Object object) { 253 | if (object == this) throw new JSONException(); 254 | if (index < 0 || index >= count) { 255 | throw new JSONException("Index out of bounds: " + index); 256 | } 257 | elements[index] = JSON.getJSON(object); 258 | } 259 | 260 | public void set(int index, AbstractJSON json) { 261 | if (json == this) throw new JSONException(); 262 | if (index < 0 || index >= count) { 263 | throw new JSONException("Index out of bounds: " + index); 264 | } 265 | elements[index] = json; 266 | } 267 | 268 | public void set(int index, String s) { 269 | if (index < 0 || index >= count) { 270 | throw new JSONException("Index out of bounds: " + index); 271 | } 272 | elements[index] = s; 273 | } 274 | 275 | public void set(int index, int i) { 276 | if (index < 0 || index >= count) { 277 | throw new JSONException("Index out of bounds: " + index); 278 | } 279 | elements[index] = new Integer(i); 280 | } 281 | 282 | public void set(int index, long l) { 283 | if (index < 0 || index >= count) { 284 | throw new JSONException("Index out of bounds: " + index); 285 | } 286 | elements[index] = new Long(l); 287 | } 288 | 289 | public void set(int index, double d) { 290 | if (index < 0 || index >= count) { 291 | throw new JSONException("Index out of bounds: " + index); 292 | } 293 | elements[index] = new Double(d); 294 | } 295 | 296 | public void set(int index, boolean b) { 297 | if (index < 0 || index >= count) { 298 | throw new JSONException("Index out of bounds: " + index); 299 | } 300 | elements[index] = new Boolean(b); 301 | } 302 | 303 | /** 304 | * @deprecated 305 | */ 306 | public void put(int index, Object object) { 307 | if (object == this) throw new JSONException(); 308 | insertElementAt(JSON.getJSON(object), index); 309 | } 310 | 311 | public void put(int index, AbstractJSON json) { 312 | if (json == this) throw new JSONException(); 313 | insertElementAt(json, index); 314 | } 315 | 316 | public void put(int index, String s) { 317 | insertElementAt(s, index); 318 | } 319 | 320 | public void put(int index, int i) { 321 | insertElementAt(new Integer(i), index); 322 | } 323 | 324 | public void put(int index, long l) { 325 | insertElementAt(new Long(l), index); 326 | } 327 | 328 | public void put(int index, double d) { 329 | insertElementAt(new Double(d), index); 330 | } 331 | 332 | public void put(int index, boolean b) { 333 | insertElementAt(new Boolean(b), index); 334 | } 335 | 336 | public boolean has(Object object) { 337 | return _indexOf(JSON.getJSON(object), 0) != -1; 338 | } 339 | 340 | public boolean has(int i) { 341 | return _indexOf(new Integer(i), 0) != -1; 342 | } 343 | 344 | public boolean has(long l) { 345 | return _indexOf(new Long(l), 0) != -1; 346 | } 347 | 348 | public boolean has(double d) { 349 | return _indexOf(new Double(d), 0) != -1; 350 | } 351 | 352 | public boolean has(boolean b) { 353 | return _indexOf(new Boolean(b), 0) != -1; 354 | } 355 | 356 | public int indexOf(Object object) { 357 | return _indexOf(JSON.getJSON(object), 0); 358 | } 359 | 360 | public int indexOf(Object object, int index) { 361 | return _indexOf(JSON.getJSON(object), index); 362 | } 363 | 364 | public void clear() { 365 | for (int i = 0; i < count; i++) elements[i] = null; 366 | count = 0; 367 | } 368 | 369 | public boolean remove(Object object) { 370 | int i = _indexOf(JSON.getJSON(object), 0); 371 | if (i == -1) return false; 372 | remove(i); 373 | return true; 374 | } 375 | 376 | public void remove(int index) { 377 | if (index < 0 || index >= count) { 378 | throw new JSONException("Index out of bounds: " + index); 379 | } 380 | count--; 381 | int size = count - index; 382 | if (size > 0) 383 | System.arraycopy(elements, index + 1, elements, index, size); 384 | elements[count] = null; 385 | } 386 | 387 | public int size() { 388 | return count; 389 | } 390 | 391 | public boolean isEmpty() { 392 | return count == 0; 393 | } 394 | 395 | public String toString() { 396 | return build(); 397 | } 398 | 399 | public boolean equals(Object obj) { 400 | return this == obj || super.equals(obj) || similar(obj); 401 | } 402 | 403 | public boolean similar(Object obj) { 404 | if (!(obj instanceof JSONArray)) { 405 | return false; 406 | } 407 | int size = count; 408 | if (size != ((JSONArray)obj).count) { 409 | return false; 410 | } 411 | for (int i = 0; i < size; i++) { 412 | Object a = get(i); 413 | Object b = ((JSONArray)obj).get(i); 414 | if (a == b) { 415 | continue; 416 | } 417 | if (a == null) { 418 | return false; 419 | } 420 | if (a instanceof AbstractJSON) { 421 | if (!((AbstractJSON)a).similar(b)) { 422 | return false; 423 | } 424 | } else if (!a.equals(b)) { 425 | return false; 426 | } 427 | } 428 | return true; 429 | } 430 | 431 | public String build() { 432 | int size = count; 433 | if (size == 0) 434 | return "[]"; 435 | StringBuffer s = new StringBuffer("["); 436 | int i = 0; 437 | while (i < size) { 438 | Object v = elements[i]; 439 | if (v instanceof AbstractJSON) { 440 | s.append(((AbstractJSON) v).build()); 441 | } else if (v instanceof String) { 442 | s.append("\"").append(JSON.escape_utf8((String) v)).append("\""); 443 | } else if (v instanceof String[]) { 444 | s.append(((String[]) v)[0]); 445 | } else if (v == JSON.json_null) { 446 | s.append((String) null); 447 | } else { 448 | s.append(String.valueOf(v)); 449 | } 450 | i++; 451 | if (i < size) { 452 | s.append(","); 453 | } 454 | } 455 | s.append("]"); 456 | return s.toString(); 457 | } 458 | 459 | protected String format(int l) { 460 | int size = count; 461 | if (size == 0) 462 | return "[]"; 463 | String t = ""; 464 | for (int i = 0; i < l; i++) { 465 | t = t.concat(JSON.FORMAT_TAB); 466 | } 467 | String t2 = t.concat(JSON.FORMAT_TAB); 468 | StringBuffer s = new StringBuffer("[\n"); 469 | s.append(t2); 470 | int i = 0; 471 | while (i < size) { 472 | Object v = elements[i]; 473 | if (v instanceof String[]) { 474 | v = elements[i] = JSON.parseJSON(((String[]) v)[0]); 475 | } 476 | if (v instanceof AbstractJSON) { 477 | s.append(((AbstractJSON) v).format(l + 1)); 478 | } else if (v instanceof String) { 479 | s.append("\"").append(JSON.escape_utf8((String) v)).append("\""); 480 | } else if (v == JSON.json_null) { 481 | s.append((String) null); 482 | } else { 483 | s.append(v); 484 | } 485 | i++; 486 | if (i < size) { 487 | s.append(",\n").append(t2); 488 | } 489 | } 490 | if (l > 0) { 491 | s.append("\n").append(t).append("]"); 492 | } else { 493 | s.append("\n]"); 494 | } 495 | return s.toString(); 496 | } 497 | 498 | public void write(OutputStream out) throws IOException { 499 | int size = count; 500 | out.write((byte) '['); 501 | if (size == 0) { 502 | out.write((byte) ']'); 503 | return; 504 | } 505 | int i = 0; 506 | while (i < size) { 507 | Object v = elements[i]; 508 | if (v instanceof JSONObject) { 509 | ((JSONObject) v).write(out); 510 | } else if (v instanceof JSONArray) { 511 | ((JSONArray) v).write(out); 512 | } else if (v instanceof String) { 513 | out.write((byte) '"'); 514 | JSON.writeString(out, (String) v); 515 | out.write((byte) '"'); 516 | } else if (v instanceof String[]) { 517 | out.write((((String[]) v)[0]).getBytes("UTF-8")); 518 | } else if (v == JSON.json_null) { 519 | out.write((byte) 'n'); 520 | out.write((byte) 'u'); 521 | out.write((byte) 'l'); 522 | out.write((byte) 'l'); 523 | } else { 524 | out.write(String.valueOf(v).getBytes("UTF-8")); 525 | } 526 | i++; 527 | if (i < size) { 528 | out.write((byte) ','); 529 | } 530 | } 531 | out.write((byte) '}'); 532 | } 533 | 534 | public Enumeration elements() { 535 | return new Enumeration() { 536 | int i = 0; 537 | 538 | public boolean hasMoreElements() { 539 | return i < count; 540 | } 541 | 542 | public Object nextElement() { 543 | Object o = elements[i]; 544 | if (o instanceof String[]) 545 | o = elements[i] = JSON.parseJSON(((String[]) o)[0]); 546 | i++; 547 | return o == JSON.json_null ? null : o; 548 | } 549 | }; 550 | } 551 | 552 | public void copyInto(Object[] arr) { 553 | copyInto(arr, 0, arr.length); 554 | } 555 | 556 | public void copyInto(Object[] arr, int offset, int length) { 557 | int i = offset; 558 | int j = 0; 559 | while(i < arr.length && j < length && j < size()) { 560 | arr[i++] = get(j++); 561 | } 562 | } 563 | 564 | public Vector toVector() { 565 | int size = count; 566 | Vector copy = new Vector(size); 567 | for (int i = 0; i < size; i++) { 568 | Object o = elements[i]; 569 | if (o instanceof String[]) 570 | o = elements[i] = JSON.parseJSON(((String[]) o)[0]); 571 | if (o instanceof JSONObject) { 572 | o = ((JSONObject) o).toTable(); 573 | } else if (o instanceof JSONArray) { 574 | o = ((JSONArray) o).toVector(); 575 | } 576 | copy.addElement(o); 577 | } 578 | return copy; 579 | } 580 | 581 | void addElement(Object object) { 582 | if (count == elements.length) grow(); 583 | elements[count++] = object; 584 | } 585 | 586 | private void insertElementAt(Object object, int index) { 587 | if (index < 0 || index > count) { 588 | throw new JSONException("Index out of bounds: " + index); 589 | } 590 | if (count == elements.length) grow(); 591 | int size = count - index; 592 | if (size > 0) 593 | System.arraycopy(elements, index, elements, index + 1, size); 594 | elements[index] = object; 595 | count++; 596 | } 597 | 598 | private int _indexOf(Object object, int start) { 599 | for (int i = start; i < count; i++) { 600 | if (elements[i] instanceof String[]) 601 | elements[i] = JSON.parseJSON(((String[]) elements[i])[0]); 602 | if (object.equals(elements[i])) return i; 603 | } 604 | return -1; 605 | } 606 | 607 | private void grow() { 608 | Object[] tmp = new Object[elements.length * 2]; 609 | System.arraycopy(elements, 0, tmp, 0, count); 610 | elements = tmp; 611 | } 612 | 613 | } 614 | -------------------------------------------------------------------------------- /src/cc/nnproject/json/JSONException.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2021-2024 Arman Jussupgaliyev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | package cc.nnproject.json; 23 | 24 | public class JSONException extends RuntimeException { 25 | 26 | public JSONException() { 27 | } 28 | 29 | public JSONException(String msg) { 30 | super(msg); 31 | } 32 | 33 | public String toString() { 34 | return getMessage() == null ? "JSONException" : "JSONException: " + getMessage(); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/cc/nnproject/json/JSONObject.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2021-2025 Arman Jussupgaliyev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | package cc.nnproject.json; 23 | 24 | import java.io.IOException; 25 | import java.io.OutputStream; 26 | import java.util.Enumeration; 27 | import java.util.Hashtable; 28 | 29 | public class JSONObject extends AbstractJSON { 30 | 31 | protected Hashtable table; 32 | 33 | public JSONObject() { 34 | table = new Hashtable(); 35 | } 36 | 37 | /** 38 | * @deprecated Doesn't adapt nested elements 39 | */ 40 | public JSONObject(Hashtable table) { 41 | this.table = table; 42 | } 43 | 44 | /** 45 | * @deprecated Compatibility with org.json 46 | */ 47 | public JSONObject(String str) { 48 | table = JSON.getObject(str).table; // FIXME 49 | } 50 | 51 | public Object get(String name) throws JSONException { 52 | try { 53 | if (has(name)) { 54 | Object o = table.get(name); 55 | if (o instanceof String[]) 56 | table.put(name, o = JSON.parseJSON(((String[]) o)[0])); 57 | if (o == JSON.json_null) 58 | return null; 59 | return o; 60 | } 61 | } catch (JSONException e) { 62 | throw e; 63 | } catch (Exception e) {} 64 | throw new JSONException("No value for name: " + name); 65 | } 66 | 67 | // unused methods should be removed by proguard shrinking 68 | 69 | public Object get(String name, Object def) { 70 | if (!has(name)) return def; 71 | try { 72 | return get(name); 73 | } catch (Exception e) { 74 | return def; 75 | } 76 | } 77 | 78 | public Object getNullable(String name) { 79 | return get(name, null); 80 | } 81 | 82 | public String getString(String name) throws JSONException { 83 | Object o = get(name); 84 | if (o == null || o instanceof String) 85 | return (String) o; 86 | return String.valueOf(o); 87 | } 88 | 89 | public String getString(String name, String def) { 90 | try { 91 | Object o = get(name, def); 92 | if (o == null || o instanceof String) 93 | return (String) o; 94 | return String.valueOf(o); 95 | } catch (Exception e) { 96 | return def; 97 | } 98 | } 99 | 100 | public String getNullableString(String name) { 101 | return getString(name, null); 102 | } 103 | 104 | public JSONObject getObject(String name) throws JSONException { 105 | try { 106 | return (JSONObject) get(name); 107 | } catch (ClassCastException e) { 108 | throw new JSONException("Not object: " + name); 109 | } 110 | } 111 | public JSONObject getObject(String name, JSONObject def) { 112 | if (has(name)) { 113 | try { 114 | return (JSONObject) get(name); 115 | } catch (Exception e) {} 116 | } 117 | return def; 118 | } 119 | 120 | public JSONObject getNullableObject(String name) { 121 | return getObject(name, null); 122 | } 123 | 124 | public JSONArray getArray(String name) throws JSONException { 125 | try { 126 | return (JSONArray) get(name); 127 | } catch (ClassCastException e) { 128 | throw new JSONException("Not array: " + name); 129 | } 130 | } 131 | 132 | public JSONArray getArray(String name, JSONArray def) throws JSONException { 133 | if (has(name)) { 134 | try { 135 | return (JSONArray) get(name); 136 | } catch (Exception e) { 137 | } 138 | } 139 | return def; 140 | } 141 | 142 | 143 | public JSONArray getNullableArray(String name) { 144 | return getArray(name, null); 145 | } 146 | 147 | public int getInt(String name) throws JSONException { 148 | return JSON.getInt(get(name)); 149 | } 150 | 151 | public int getInt(String name, int def) { 152 | if (!has(name)) return def; 153 | try { 154 | return getInt(name); 155 | } catch (Exception e) { 156 | return def; 157 | } 158 | } 159 | 160 | public long getLong(String name) throws JSONException { 161 | return JSON.getLong(get(name)); 162 | } 163 | 164 | public long getLong(String name, long def) { 165 | if (!has(name)) return def; 166 | try { 167 | return getLong(name); 168 | } catch (Exception e) { 169 | return def; 170 | } 171 | } 172 | 173 | public double getDouble(String name) throws JSONException { 174 | return JSON.getDouble(get(name)); 175 | } 176 | 177 | public double getDouble(String name, double def) { 178 | if (!has(name)) return def; 179 | try { 180 | return getDouble(name); 181 | } catch (Exception e) { 182 | return def; 183 | } 184 | } 185 | 186 | public boolean getBoolean(String name) throws JSONException { 187 | Object o = get(name); 188 | if (o == JSON.TRUE) return true; 189 | if (o == JSON.FALSE) return false; 190 | if (o instanceof Boolean) return ((Boolean) o).booleanValue(); 191 | if (o instanceof String) { 192 | String s = (String) o; 193 | s = s.toLowerCase(); 194 | if (s.equals("true")) return true; 195 | if (s.equals("false")) return false; 196 | } 197 | throw new JSONException("Not boolean: " + o); 198 | } 199 | 200 | public boolean getBoolean(String name, boolean def) { 201 | if (!has(name)) return def; 202 | try { 203 | return getBoolean(name); 204 | } catch (Exception e) { 205 | return def; 206 | } 207 | } 208 | 209 | public boolean isNull(String name) { 210 | if (!has(name)) 211 | throw new JSONException("No value for name: " + name); 212 | return table.get(name) == JSON.json_null; 213 | } 214 | 215 | /** 216 | * @deprecated 217 | */ 218 | public void put(String name, Object obj) { 219 | table.put(name, JSON.getJSON(obj)); 220 | } 221 | 222 | public void put(String name, AbstractJSON json) { 223 | table.put(name, json == null ? JSON.json_null : json); 224 | } 225 | 226 | public void put(String name, String s) { 227 | table.put(name, s == null ? JSON.json_null : s); 228 | } 229 | 230 | public void put(String name, int i) { 231 | table.put(name, new Integer(i)); 232 | } 233 | 234 | public void put(String name, long l) { 235 | table.put(name, new Long(l)); 236 | } 237 | 238 | public void put(String name, double d) { 239 | table.put(name, new Double(d)); 240 | } 241 | 242 | public void put(String name, boolean b) { 243 | table.put(name, new Boolean(b)); 244 | } 245 | 246 | public boolean hasValue(Object object) { 247 | return table.contains(JSON.getJSON(object)); 248 | } 249 | 250 | // hasKey 251 | public boolean has(String name) { 252 | return table.containsKey(name); 253 | } 254 | 255 | public void clear() { 256 | table.clear(); 257 | } 258 | 259 | public void remove(String name) { 260 | table.remove(name); 261 | } 262 | 263 | public int size() { 264 | return table.size(); 265 | } 266 | 267 | public boolean isEmpty() { 268 | return table.isEmpty(); 269 | } 270 | 271 | public String toString() { 272 | return build(); 273 | } 274 | 275 | public boolean equals(Object obj) { 276 | return this == obj || super.equals(obj) || similar(obj); 277 | } 278 | 279 | public boolean similar(Object obj) { 280 | if (!(obj instanceof JSONObject)) { 281 | return false; 282 | } 283 | if (table.equals(((JSONObject) obj).table)) { 284 | return true; 285 | } 286 | int size = size(); 287 | if (size != ((JSONObject)obj).size()) { 288 | return false; 289 | } 290 | Enumeration keys = table.keys(); 291 | while (keys.hasMoreElements()) { 292 | String key = (String) keys.nextElement(); 293 | Object a = get(key); 294 | Object b = ((JSONObject)obj).get(key); 295 | if (a == b) { 296 | continue; 297 | } 298 | if (a == null) { 299 | return false; 300 | } 301 | if (a instanceof AbstractJSON) { 302 | if (!((AbstractJSON)a).similar(b)) { 303 | return false; 304 | } 305 | } else if (!a.equals(b)) { 306 | return false; 307 | } 308 | } 309 | return true; 310 | } 311 | 312 | public String build() { 313 | if (size() == 0) 314 | return "{}"; 315 | StringBuffer s = new StringBuffer("{"); 316 | Enumeration keys = table.keys(); 317 | while (true) { 318 | String k = (String) keys.nextElement(); 319 | s.append("\"").append(k).append("\":"); 320 | Object v = table.get(k); 321 | if (v instanceof AbstractJSON) { 322 | s.append(((AbstractJSON) v).build()); 323 | } else if (v instanceof String) { 324 | s.append("\"").append(JSON.escape_utf8((String) v)).append("\""); 325 | } else if (v instanceof String[]) { 326 | s.append(((String[]) v)[0]); 327 | } else if (v == JSON.json_null) { 328 | s.append((String) null); 329 | } else { 330 | s.append(v); 331 | } 332 | if (!keys.hasMoreElements()) { 333 | break; 334 | } 335 | s.append(","); 336 | } 337 | s.append("}"); 338 | return s.toString(); 339 | } 340 | 341 | protected String format(int l) { 342 | int size = size(); 343 | if (size == 0) 344 | return "{}"; 345 | String t = ""; 346 | for (int i = 0; i < l; i++) { 347 | t = t.concat(JSON.FORMAT_TAB); 348 | } 349 | String t2 = t.concat(JSON.FORMAT_TAB); 350 | StringBuffer s = new StringBuffer("{\n"); 351 | s.append(t2); 352 | Enumeration keys = table.keys(); 353 | int i = 0; 354 | while (keys.hasMoreElements()) { 355 | String k = (String) keys.nextElement(); 356 | s.append("\"").append(k).append("\": "); 357 | Object v = get(k); 358 | if (v instanceof String[]) 359 | table.put(k, v = JSON.parseJSON(((String[]) v)[0])); 360 | if (v instanceof AbstractJSON) { 361 | s.append(((AbstractJSON) v).format(l + 1)); 362 | } else if (v instanceof String) { 363 | s.append("\"").append(JSON.escape_utf8((String) v)).append("\""); 364 | } else if (v == JSON.json_null) { 365 | s.append((String) null); 366 | } else { 367 | s.append(v); 368 | } 369 | i++; 370 | if (i < size) { 371 | s.append(",\n").append(t2); 372 | } 373 | } 374 | if (l > 0) { 375 | s.append("\n").append(t).append("}"); 376 | } else { 377 | s.append("\n}"); 378 | } 379 | return s.toString(); 380 | } 381 | 382 | public void write(OutputStream out) throws IOException { 383 | out.write((byte) '{'); 384 | if (size() == 0) { 385 | out.write((byte) '}'); 386 | return; 387 | } 388 | Enumeration keys = table.keys(); 389 | while (true) { 390 | String k = (String) keys.nextElement(); 391 | out.write((byte) '"'); 392 | JSON.writeString(out, k.toString()); 393 | out.write((byte) '"'); 394 | out.write((byte) ':'); 395 | Object v = table.get(k); 396 | if (v instanceof JSONObject) { 397 | ((JSONObject) v).write(out); 398 | } else if (v instanceof JSONArray) { 399 | ((JSONArray) v).write(out); 400 | } else if (v instanceof String) { 401 | out.write((byte) '"'); 402 | JSON.writeString(out, (String) v); 403 | out.write((byte) '"'); 404 | } else if (v instanceof String[]) { 405 | out.write((((String[]) v)[0]).getBytes("UTF-8")); 406 | } else if (v == JSON.json_null) { 407 | out.write((byte) 'n'); 408 | out.write((byte) 'u'); 409 | out.write((byte) 'l'); 410 | out.write((byte) 'l'); 411 | } else { 412 | out.write(String.valueOf(v).getBytes("UTF-8")); 413 | } 414 | if (!keys.hasMoreElements()) { 415 | break; 416 | } 417 | out.write((byte) ','); 418 | } 419 | out.write((byte) '}'); 420 | } 421 | 422 | public Enumeration keys() { 423 | return table.keys(); 424 | } 425 | 426 | public JSONArray keysAsArray() { 427 | JSONArray array = new JSONArray(table.size()); 428 | Enumeration keys = table.keys(); 429 | while (keys.hasMoreElements()) { 430 | array.addElement(keys.nextElement()); 431 | } 432 | return array; 433 | } 434 | 435 | /** 436 | * @deprecated Use {@link JSONObject#toTable()} instead 437 | */ 438 | public Hashtable getTable() { 439 | return table; 440 | } 441 | 442 | public Hashtable toTable() { 443 | Hashtable copy = new Hashtable(table.size()); 444 | Enumeration keys = table.keys(); 445 | while (keys.hasMoreElements()) { 446 | String k = (String) keys.nextElement(); 447 | Object v = table.get(k); 448 | if (v instanceof String[]) 449 | table.put(k, v = JSON.parseJSON(((String[]) v)[0])); 450 | if (v instanceof JSONObject) { 451 | v = ((JSONObject) v).toTable(); 452 | } else if (v instanceof JSONArray) { 453 | v = ((JSONArray) v).toVector(); 454 | } 455 | copy.put(k, v); 456 | } 457 | return copy; 458 | } 459 | 460 | void _put(String name, Object obj) { 461 | table.put(name, obj); 462 | } 463 | 464 | // TODO: Enumeration elements() 465 | 466 | } 467 | -------------------------------------------------------------------------------- /src/cc/nnproject/json/JSONStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2024-2025 Arman Jussupgaliyev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | package cc.nnproject.json; 23 | 24 | import java.io.IOException; 25 | import java.io.InputStream; 26 | import java.io.InputStreamReader; 27 | import java.io.Reader; 28 | 29 | // Streaming JSON 30 | 31 | public class JSONStream { 32 | 33 | public static String encoding = "UTF-8"; 34 | public static boolean buffer = true; 35 | 36 | private Reader reader; 37 | private boolean isObject; 38 | int index; 39 | private boolean eof; 40 | private char prev; 41 | private boolean usePrev; 42 | 43 | private JSONStream() {} 44 | 45 | private void init(InputStream in) throws IOException { 46 | reader = new InputStreamReader(in, "UTF-8"); 47 | if (buffer) { 48 | reader = new BufferedReader(reader); 49 | } 50 | } 51 | 52 | // Static functions 53 | 54 | public static JSONStream getStream(InputStream in) throws IOException { 55 | JSONStream json = new JSONStream(); 56 | json.init(in); 57 | char c = json.nextTrim(); 58 | if (c != '{' && c != '[') 59 | throw new JSONException("getStream: Not json"); 60 | json.isObject = c == '{'; 61 | json.usePrev = true; 62 | return json; 63 | } 64 | 65 | public static JSONStream getStream(Reader r) throws IOException { 66 | JSONStream json = new JSONStream(); 67 | json.reader = r; 68 | char c = json.nextTrim(); 69 | if (c != '{' && c != '[') 70 | throw new JSONException("getStream: Not json"); 71 | json.isObject = c == '{'; 72 | json.usePrev = true; 73 | return json; 74 | } 75 | 76 | public static AbstractJSON getJSON(InputStream in) throws IOException { 77 | JSONStream json = new JSONStream(); 78 | try { 79 | json.init(in); 80 | char c = json.nextTrim(); 81 | if (c != '[' && c != '{') 82 | throw new JSONException("getJSON: Not json"); 83 | if (c == '{') 84 | return json.nextObject(false); 85 | else 86 | return json.nextArray(false); 87 | } finally { 88 | json.close(); 89 | } 90 | } 91 | 92 | public static JSONObject getObject(InputStream in) throws IOException { 93 | JSONStream json = new JSONStream(); 94 | try { 95 | json.init(in); 96 | char c = json.nextTrim(); 97 | if (c != '{') throw new JSONException("getObject: not object"); 98 | return json.nextObject(false); 99 | } finally { 100 | json.close(); 101 | } 102 | } 103 | 104 | public static JSONArray getArray(InputStream in) throws IOException { 105 | JSONStream json = new JSONStream(); 106 | try { 107 | json.init(in); 108 | char c = json.nextTrim(); 109 | if (c != '[') throw new JSONException("getArray: not array"); 110 | return json.nextArray(false); 111 | } finally { 112 | json.close(); 113 | } 114 | } 115 | 116 | // Streaming JSON functions 117 | 118 | public Object nextValue() throws IOException { 119 | char c = nextTrim(); 120 | switch (c) { 121 | case '{': 122 | return nextObject(false); 123 | case '[': 124 | return nextArray(false); 125 | case '"': 126 | return nextString(false); 127 | case 't': // true 128 | skip(3); 129 | return JSON.TRUE; 130 | case 'f': // false 131 | skip(4); 132 | return JSON.FALSE; 133 | case 'n': // null 134 | skip(3); 135 | return JSON.json_null; 136 | default: 137 | back(); 138 | return nextValue(true); 139 | } 140 | } 141 | 142 | public JSONObject nextObject() throws IOException { 143 | return nextObject(true); 144 | } 145 | 146 | public JSONArray nextArray() throws IOException { 147 | return nextArray(true); 148 | } 149 | 150 | public String nextString() throws IOException { 151 | return nextString(true); 152 | } 153 | 154 | public Object nextNumber() throws IOException { 155 | Object v = nextValue(true); 156 | if (v instanceof String) 157 | throw new JSONException("nextNumber: not number: ".concat(String.valueOf(v))); 158 | return v; 159 | } 160 | 161 | public boolean isObject() { 162 | return isObject; 163 | } 164 | 165 | public boolean isArray() { 166 | return !isObject; 167 | } 168 | 169 | // Search functions 170 | 171 | // Jumps to key in object 172 | // Result is found, if false will skip to the end of object 173 | public boolean jumpToKey(String key) throws IOException { 174 | // if (!isObject) 175 | // throw new JSONException("jumpToKey: not object"); 176 | 177 | char c; 178 | // while((c = nextTrim()) != '"' && c != 0); 179 | // back(); 180 | 181 | while (true) { 182 | c = nextTrim(); 183 | if (c == ',') continue; 184 | if (c != '"') 185 | throw new RuntimeException("JSON: jumpToKey: malformed object at ".concat(Integer.toString(index))); 186 | back(); 187 | if (nextString(true).equals(key)) { 188 | // jump to value 189 | if (nextTrim() != ':') 190 | throw new JSONException("jumpToKey: malformed object at ".concat(Integer.toString(index))); 191 | return true; 192 | } 193 | if (nextTrim() != ':') 194 | throw new JSONException("jumpToKey: malformed object at ".concat(Integer.toString(index))); 195 | 196 | // skipValue(); 197 | c = nextTrim(); 198 | 199 | switch (c) { 200 | case '{': 201 | skipObject(); 202 | break; 203 | case '[': 204 | skipArray(); 205 | break; 206 | case '"': 207 | skipString(); 208 | break; 209 | case 0: 210 | return false; 211 | default: 212 | while ((c = next()) != 0 && c != ',' && c != '}'); 213 | back(); 214 | break; 215 | } 216 | 217 | c = nextTrim(); 218 | if (c == ',') { 219 | continue; 220 | } 221 | if (c == '}') 222 | return false; 223 | throw new JSONException("jumpToKey: malformed object at ".concat(Integer.toString(index))); 224 | } 225 | } 226 | 227 | // Skip N elements in array 228 | // If param is less than 1 or bigger than left elements count, will skip to the end of array 229 | // Result is success 230 | public boolean skipArrayElements(int count) throws IOException { 231 | while (true) { 232 | char c = nextTrim(); 233 | switch (c) { 234 | case ']': 235 | return false; 236 | case '{': 237 | skipObject(); 238 | break; 239 | case '[': 240 | skipArray(); 241 | break; 242 | case '"': 243 | skipString(); 244 | break; 245 | case 0: 246 | return false; 247 | default: 248 | while ((c = next()) != 0 && c != ',' && c != ']'); 249 | back(); 250 | break; 251 | } 252 | c = nextTrim(); 253 | if (c == ',') { 254 | if (--count == 0) 255 | return true; 256 | continue; 257 | } 258 | return false; 259 | } 260 | } 261 | 262 | // public boolean jumpToKeyGlobal(String key) throws IOException { 263 | // char c = 0; 264 | // boolean p = false; 265 | // boolean q = false; 266 | // boolean e = false; 267 | // while(true) { 268 | // if (p) { 269 | // p = false; 270 | // } else c = next(); 271 | // if (c == 0) return false; 272 | // if (!e) { 273 | // if (c == '\\') e = true; 274 | // else if (c == '"') q = !q; 275 | // } else e = false; 276 | // if (!q) 277 | // if (c == '{' || c == ',') { 278 | // if ((c = next()) == '\"') { 279 | // back(); 280 | // String s = nextString(); 281 | // if (nextTrim() != ':') 282 | // throw new JSONException("jumpToKey: malformed object at ".concat(Integer.toString(index))); 283 | // if (key.equals(s)) return true; 284 | // } else p = true; 285 | // } 286 | // } 287 | // } 288 | 289 | // 290 | 291 | public void skipValue() throws IOException { 292 | char c = nextTrim(); 293 | switch (c) { 294 | case '{': 295 | skipObject(); 296 | break; 297 | case '[': 298 | skipArray(); 299 | break; 300 | case '"': 301 | skipString(); 302 | break; 303 | case 0: 304 | return; 305 | default: 306 | while ((c = next()) != 0 && c != ',' && c != ':' && c != '}' && c != ']'); 307 | back(); 308 | break; 309 | } 310 | } 311 | 312 | // Basic reader functions 313 | 314 | public char next() throws IOException { 315 | if (usePrev) { 316 | usePrev = false; 317 | index++; 318 | return prev; 319 | } 320 | // if (eof) return 0; 321 | int r = reader.read(); 322 | if (r <= 0) { 323 | eof = true; 324 | return 0; 325 | } 326 | index++; 327 | return prev = (char) r; 328 | } 329 | 330 | public char nextTrim() throws IOException { 331 | char c; 332 | while ((c = next()) <= ' ' && c != 0); 333 | return c; 334 | } 335 | 336 | public void skip(int n) throws IOException { 337 | if (usePrev) { 338 | usePrev = false; 339 | n--; 340 | } 341 | index += n; 342 | reader.skip(n); 343 | } 344 | 345 | public void back() { 346 | if (usePrev || index <= 0) throw new JSONException("back"); 347 | usePrev = true; 348 | index--; 349 | } 350 | 351 | public boolean end() { 352 | return eof; 353 | } 354 | 355 | public void expectNext(char c) throws IOException { 356 | char n; 357 | if ((n = next()) != c) 358 | throw new JSONException("Expected '" + c + "', but got '" + n + "' at " + (index-1)); 359 | } 360 | 361 | public void expectNextTrim(char c) throws IOException { 362 | char n; 363 | if ((n = nextTrim()) != c) 364 | throw new JSONException("Expected '" + c + "', but got '" + n + "' at " + (index-1)); 365 | } 366 | 367 | /** 368 | * @deprecated mark is probably not supported, since target is CLDC 369 | */ 370 | public void reset() throws IOException { 371 | index = prev = 0; 372 | usePrev = false; 373 | reader.reset(); 374 | } 375 | 376 | public void reset(InputStream is) throws IOException { 377 | try { 378 | close(); 379 | } catch (IOException e) {} 380 | index = prev = 0; 381 | usePrev = false; 382 | init(is); 383 | } 384 | 385 | public void close() throws IOException { 386 | reader.close(); 387 | } 388 | 389 | // 390 | 391 | private JSONObject nextObject(boolean check) throws IOException { 392 | if (check && nextTrim() != '{') { 393 | back(); 394 | throw new JSONException("nextObject: not object at ".concat(Integer.toString(index))); 395 | } 396 | JSONObject r = new JSONObject(); 397 | object: { 398 | while (true) { 399 | char c = nextTrim(); 400 | if (c == '}') break object; 401 | back(); 402 | String key = nextString(true); 403 | if (nextTrim() != ':') 404 | throw new JSONException("nextObject: malformed object at ".concat(Integer.toString(index))); 405 | Object val = null; 406 | c = nextTrim(); 407 | switch (c) { 408 | case '}': 409 | break object; 410 | case '{': 411 | val = nextObject(false); 412 | break; 413 | case '[': 414 | val = nextArray(false); 415 | break; 416 | case '"': 417 | val = nextString(false); 418 | break; 419 | case 'n': // null 420 | skip(3); 421 | val = JSON.json_null; 422 | break; 423 | case 't': // true 424 | skip(3); 425 | val = JSON.TRUE; 426 | break; 427 | case 'f': // false 428 | skip(4); 429 | val = JSON.FALSE; 430 | break; 431 | default: 432 | back(); 433 | val = nextValue(true); 434 | break; 435 | } 436 | r.put(key, val); 437 | c = nextTrim(); 438 | if (c == ',') { 439 | continue; 440 | } 441 | if (c == '}') break; 442 | throw new JSONException("nextObject: malformed object at ".concat(Integer.toString(index))); 443 | } 444 | } 445 | if (eof) 446 | throw new IOException("nextObject: Unexpected end"); 447 | return r; 448 | } 449 | 450 | private JSONArray nextArray(boolean check) throws IOException { 451 | if (check && nextTrim() != '[') { 452 | back(); 453 | throw new JSONException("nextArray: not array at ".concat(Integer.toString(index))); 454 | } 455 | JSONArray r = new JSONArray(); 456 | array: { 457 | while (true) { 458 | Object val = null; 459 | char c = nextTrim(); 460 | switch (c) { 461 | case ']': 462 | break array; 463 | case '{': 464 | val = nextObject(false); 465 | break; 466 | case '[': 467 | val = nextArray(false); 468 | break; 469 | case '"': 470 | val = nextString(false); 471 | break; 472 | case 'n': // null 473 | skip(3); 474 | val = JSON.json_null; 475 | break; 476 | case 't': // true 477 | skip(3); 478 | val = JSON.TRUE; 479 | break; 480 | case 'f': // false 481 | skip(4); 482 | val = JSON.FALSE; 483 | break; 484 | default: 485 | back(); 486 | val = nextValue(true); 487 | break; 488 | } 489 | r.addElement(val); 490 | c = nextTrim(); 491 | if (c == ',') { 492 | continue; 493 | } 494 | if (c == ']') break; 495 | throw new JSONException("nextArray: malformed array at ".concat(Integer.toString(index))); 496 | } 497 | } 498 | if (eof) 499 | throw new IOException("nextArray: Unexpected end"); 500 | return r; 501 | } 502 | 503 | private String nextString(boolean check) throws IOException { 504 | if (check && nextTrim() != '"') { 505 | back(); 506 | throw new JSONException("nextString: not string at ".concat(Integer.toString(index))); 507 | } 508 | StringBuffer sb = new StringBuffer(); 509 | char l = 0; 510 | while (true) { 511 | char c = next(); 512 | if (c == '\\' && l != '\\') { 513 | l = c; 514 | continue; 515 | } 516 | if (l == '\\') { 517 | if (c == 'u') { 518 | char[] chars = new char[4]; 519 | chars[0] = next(); 520 | chars[1] = next(); 521 | chars[2] = next(); 522 | chars[3] = next(); 523 | sb.append(l = (char) Integer.parseInt(new String(chars), 16)); 524 | continue; 525 | } 526 | if (c == 'n') { 527 | sb.append(l = '\n'); 528 | continue; 529 | } 530 | if (c == 'r') { 531 | sb.append(l = '\r'); 532 | continue; 533 | } 534 | if (c == 't') { 535 | sb.append(l = '\t'); 536 | continue; 537 | } 538 | if (c == 'f') { 539 | sb.append(l = '\f'); 540 | continue; 541 | } 542 | if (c == 'b') { 543 | sb.append(l = '\b'); 544 | continue; 545 | } 546 | } 547 | if (c == 0 || (l != '\\' && c == '"')) break; 548 | sb.append(c); 549 | l = c; 550 | } 551 | if (eof) 552 | throw new IOException("nextString: Unexpected end"); 553 | return sb.toString(); 554 | } 555 | 556 | private void skipObject() throws IOException { 557 | while (true) { 558 | if (nextTrim() != '"') 559 | throw new JSONException("skipObject: malformed object at ".concat(Integer.toString(index))); 560 | skipString(); 561 | if (nextTrim() != ':') 562 | throw new JSONException("skipObject: malformed object at ".concat(Integer.toString(index))); 563 | char c = nextTrim(); 564 | switch (c) { 565 | case '}': 566 | return; 567 | case '{': 568 | skipObject(); 569 | break; 570 | case '[': 571 | skipArray(); 572 | break; 573 | case '"': 574 | skipString(); 575 | break; 576 | case 0: 577 | return; 578 | default: 579 | while ((c = next()) != 0 && c != ',' && c != ':' && c != '}'); 580 | back(); 581 | break; 582 | } 583 | c = nextTrim(); 584 | if (c == ',') { 585 | continue; 586 | } 587 | if (c == '}') return; 588 | throw new JSONException("skipObject: malformed object at ".concat(Integer.toString(index))); 589 | } 590 | } 591 | 592 | private void skipArray() throws IOException { 593 | while (true) { 594 | char c = nextTrim(); 595 | switch (c) { 596 | case ']': 597 | return; 598 | case '{': 599 | skipObject(); 600 | break; 601 | case '[': 602 | skipArray(); 603 | break; 604 | case '"': 605 | skipString(); 606 | break; 607 | case 0: 608 | return; 609 | default: 610 | while ((c = next()) != 0 && c != ',' && c != ']'); 611 | back(); 612 | break; 613 | } 614 | c = nextTrim(); 615 | if (c == ',') { 616 | continue; 617 | } 618 | return; 619 | } 620 | } 621 | 622 | private void skipString() throws IOException { 623 | char l = 0; 624 | while (true) { 625 | char c = next(); 626 | if (c == 0 || (l != '\\' && c == '"')) break; 627 | l = c; 628 | } 629 | } 630 | 631 | private Object nextValue(boolean convertToNumber) throws IOException { 632 | StringBuffer sb = new StringBuffer(); 633 | while (true) { 634 | char c = next(); 635 | if (c == 0) throw new JSONException("nextValue: Unexpected end"); 636 | if (c == ',' || c == ']' || c == '}' || c == ':' || c <= ' ') { 637 | back(); 638 | break; 639 | } 640 | sb.append(c); 641 | } 642 | String str = sb.toString(); 643 | if (convertToNumber) { 644 | char first = str.charAt(0); 645 | int length = str.length(); 646 | if ((first >= '0' && first <= '9') || first == '-') { 647 | try { 648 | // hex 649 | if (length > 1 && first == '0' && str.charAt(1) == 'x') { 650 | if (length > 9) // str.length() > 10 651 | return new Long(Long.parseLong(str.substring(2), 16)); 652 | return new Integer(Integer.parseInt(str.substring(2), 16)); 653 | } 654 | // decimal 655 | if (str.indexOf('.') != -1 || str.indexOf('E') != -1 || "-0".equals(str)) 656 | return new Double(Double.parseDouble(str)); 657 | if (first == '-') length--; 658 | if (length > 8) // (str.length() - (str.charAt(0) == '-' ? 1 : 0)) >= 10 659 | return new Long(Long.parseLong(str)); 660 | return new Integer(Integer.parseInt(str)); 661 | } catch (Exception ignored) {} 662 | } 663 | } 664 | return str; 665 | } 666 | 667 | } 668 | --------------------------------------------------------------------------------