├── src └── iris │ └── json │ ├── IrisJson.java │ ├── IrisJsonNull.java │ ├── IrisSequence.java │ ├── IrisJsonArray.java │ ├── IrisJsonValue.java │ ├── test │ ├── UseTest.java │ └── SpeedTest.java │ ├── IrisJsonString.java │ ├── IrisJsonObject.java │ ├── IrisJsonItem.java │ └── IrisJsonParser.java ├── CHANGELOG.md ├── README.md └── test.json /src/iris/json/IrisJson.java: -------------------------------------------------------------------------------- 1 | package iris.json; 2 | 3 | /** Создано 14.04.2020 */ 4 | 5 | 6 | class IrisJson { 7 | 8 | enum Type { 9 | Object 10 | , Array 11 | , String 12 | , Value 13 | , Null 14 | } 15 | 16 | enum ValueType { 17 | Integer, 18 | Float, 19 | Constant // в том числе: true, false, null 20 | } 21 | } 22 | 23 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## v 0.2 4 | - `IrisJsonItem.find` selectors. 5 | - `IrisJsonItem.obj()` fixed. Now it generates full tree of target objects without proxies 6 | - `IrisJsonItem.asInt|asLong|asList|asDouble|asFloat|asBoolean|asString|asObject` methods added 7 | - `IrisJsonString.obj()` fixed handling of escape characters and speed improvement 8 | - `IrisJsonItem.joinTo` method 9 | 10 | ## v 0.1 11 | - Iris JSON parser was created 12 | - Faster JSON-strings parse up to 4x times than `org.json` -------------------------------------------------------------------------------- /src/iris/json/IrisJsonNull.java: -------------------------------------------------------------------------------- 1 | package iris.json; 2 | 3 | 4 | import java.io.IOException; 5 | 6 | class IrisJsonNull extends IrisJsonItem { 7 | 8 | public IrisJsonNull() { 9 | super(IrisJson.Type.Null); 10 | } 11 | 12 | public static final IrisJsonNull Null = new IrisJsonNull(); 13 | 14 | @Override 15 | public IrisJsonItem get(int ind) { 16 | return this; 17 | } 18 | 19 | @Override 20 | public IrisJsonItem get(String key) { 21 | return this; 22 | } 23 | 24 | @Override 25 | public Object obj() { 26 | return null; 27 | } 28 | 29 | @Override 30 | public A joinTo(A buffer) throws IOException { 31 | buffer.append("null"); 32 | return buffer; 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | return "null"; 38 | } 39 | } -------------------------------------------------------------------------------- /src/iris/json/IrisSequence.java: -------------------------------------------------------------------------------- 1 | package iris.json; 2 | 3 | import java.io.IOException; 4 | import java.lang.Appendable; 5 | import java.lang.StringBuilder; 6 | 7 | class IrisSequence implements CharSequence { 8 | 9 | private final CharSequence source; 10 | private final int start; 11 | private final int end; 12 | 13 | public IrisSequence(CharSequence source, int start, int end) { 14 | this.source = source; 15 | this.start = start; 16 | this.end = end; 17 | } 18 | 19 | @Override 20 | public int length() { 21 | return end - start; 22 | } 23 | 24 | @Override 25 | public char charAt(int index) { 26 | return source.charAt(start + index); 27 | } 28 | 29 | @Override 30 | public CharSequence subSequence(int start, int end) { 31 | return new IrisSequence(source, this.start + start, this.start + end); 32 | } 33 | 34 | @Override 35 | public String toString() { 36 | return source.subSequence(start, end).toString(); 37 | } 38 | 39 | 40 | public A joinTo(A buffer) throws IOException { 41 | if (buffer instanceof StringBuilder) 42 | buffer.append(source, start, end); 43 | else 44 | buffer.append(this.toString()); 45 | return buffer; 46 | } 47 | } -------------------------------------------------------------------------------- /src/iris/json/IrisJsonArray.java: -------------------------------------------------------------------------------- 1 | package iris.json; 2 | 3 | import java.io.IOException; 4 | import java.lang.Appendable; 5 | import java.util.LinkedList; 6 | import java.util.List; 7 | 8 | class IrisJsonArray extends IrisJsonItem { 9 | 10 | private final List items; 11 | 12 | public IrisJsonArray(List items) { 13 | super(IrisJson.Type.Array); 14 | this.items = items; 15 | } 16 | 17 | @Override 18 | public A joinTo(A buffer) throws IOException { 19 | buffer.append('['); 20 | var firstDone = false; 21 | for (IrisJsonItem i : items) { 22 | if (firstDone) 23 | buffer.append(", "); 24 | else 25 | firstDone = true; 26 | i.joinTo(buffer); 27 | } 28 | 29 | buffer.append(']'); 30 | return buffer; 31 | } 32 | 33 | @Override 34 | public IrisJsonItem get(int ind) { 35 | return items.get(ind); 36 | } 37 | 38 | @Override 39 | public IrisJsonItem get(String key) { 40 | var ind = Integer.parseInt(key); 41 | return get(ind); 42 | } 43 | 44 | private Object obj; 45 | 46 | @Override 47 | public Object obj() { 48 | if (obj != null) 49 | return obj; 50 | var res = new LinkedList<>(); 51 | for (var it : items) 52 | res.add(it.obj()); 53 | obj = res; 54 | return res; 55 | } 56 | } -------------------------------------------------------------------------------- /src/iris/json/IrisJsonValue.java: -------------------------------------------------------------------------------- 1 | package iris.json; 2 | 3 | import iris.json.IrisJson.ValueType; 4 | 5 | import java.io.IOException; 6 | import java.lang.Appendable; 7 | 8 | class IrisJsonValue extends IrisJsonItem { 9 | 10 | private final IrisSequence data; 11 | private final ValueType valueType; 12 | 13 | public IrisJsonValue(IrisSequence data, ValueType valueType) { 14 | super(IrisJson.Type.Value); 15 | this.data = data; 16 | this.valueType = valueType; 17 | } 18 | 19 | @Override 20 | public A joinTo(A buffer) throws IOException { 21 | buffer.append(data.toString()); 22 | return buffer; 23 | } 24 | 25 | @Override 26 | public IrisJsonItem get(int ind) { 27 | return IrisJsonNull.Null; 28 | } 29 | 30 | @Override 31 | public IrisJsonItem get(String key) { 32 | return IrisJsonNull.Null; 33 | } 34 | 35 | 36 | 37 | private Object getReady() { 38 | if (ready != null) 39 | return ready; 40 | 41 | var s = data.toString(); 42 | return ready = switch (valueType) { 43 | case Constant -> switch (s) { 44 | case "null" -> null; 45 | case "true" -> true; 46 | case "false" -> false; 47 | default -> s; 48 | }; 49 | case Integer -> Long.valueOf(s); 50 | case Float -> Double.valueOf(s); 51 | default -> throw new IllegalArgumentException("No argument: $valueType"); 52 | }; 53 | } 54 | 55 | private Object ready; 56 | 57 | public Object obj() { 58 | return getReady(); 59 | } 60 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iris-json-parser for Java 2 | **Faster up to 4 times** parser comparing to standard org.json because of late objects initialization 3 | 4 | Speed improvement is achieved by idea of Proxy pattern, where objects are created when requested. 5 | 6 | ## Realisations for all languages 7 | - **Kotlin** (main) [iris-json-parser-kotlin](https://github.com/iris2iris/iris-json-parser-kotlin) 8 | - **Java** [iris-json-parser-java](https://github.com/iris2iris/iris-json-parser-java) 9 | 10 | ## Examples of use 11 | ```Java 12 | final var parser = new IrisJsonParser(testString); 13 | final var res = parser.parse(); // parsed to IrisJsonItem's 14 | 15 | // stringifies objects 16 | System.out.println("IrisJsonItem.toString/JSON string: " + res); 17 | 18 | // stringifies objects to Appendable buffer 19 | final var b = new StringBuilder(); 20 | res.joinTo(b); 21 | System.out.println("IrisJsonItem.joinTo/JSON string: " + b); 22 | 23 | // Simple access to required object on objects tree 24 | System.out.println("IrisJsonItem toString/JSON string: " + res.get("object").get("message").get("attachments").get(0).get("wall").get("id")); 25 | 26 | // Access by string path 27 | System.out.println("To Long: " + res.find("object message attachments 0 wall id").asLong()); 28 | 29 | // Access by string path 30 | System.out.println("To Int: " + res.find("object message attachments 0 wall id").asInt()); 31 | 32 | // Stylized to Java/JavaScript properties access 33 | System.out.println("To Double: " + res.find("object.message.attachments[0].wall.id").asDouble()); 34 | ``` 35 | 36 | Check out [CHANGELOG.md](https://github.com/iris2iris/iris-json-parser-kotlin/blob/master/CHANGELOG.md) 37 | 38 | ⭐ If this tool was useful for you, don't forget to give star. -------------------------------------------------------------------------------- /src/iris/json/test/UseTest.java: -------------------------------------------------------------------------------- 1 | package iris.json.test; 2 | 3 | import iris.json.IrisJsonParser; 4 | 5 | import java.io.FileReader; 6 | import java.io.IOException; 7 | import java.io.Reader; 8 | 9 | class UseTest { 10 | public static void main(String[] args) throws IOException { 11 | 12 | var testString = readText("test.json"); 13 | 14 | // Demonstration of functional abilities 15 | final var parser = new IrisJsonParser(testString); 16 | final var res = parser.parse(); // parsed to IrisJsonItem's 17 | 18 | // stringifies objects 19 | System.out.println("IrisJsonItem.toString/JSON string: " + res); 20 | 21 | // stringifies objects to Appendable buffer 22 | final var b = new StringBuilder(); 23 | res.joinTo(b); 24 | System.out.println("IrisJsonItem.joinTo/JSON string: " + b); 25 | 26 | // Simple access to required object on objects tree 27 | System.out.println("IrisJsonItem toString/JSON string: " + res.get("object").get("message").get("attachments").get(0).get("wall").get("id")); 28 | 29 | // Access by string path 30 | System.out.println("To Long: " + res.find("object message attachments 0 wall id").asLong()); 31 | 32 | // Access by string path 33 | System.out.println("To Int: " + res.find("object message attachments 0 wall id").asInt()); 34 | 35 | // Stylized to Java/JavaScript properties access 36 | System.out.println("To Double: " + res.find("object.message.attachments[0].wall.id").asDouble()); 37 | 38 | } 39 | 40 | 41 | 42 | static String readText(String file) throws IOException { 43 | Reader bf = new FileReader(file); 44 | StringBuilder sb = new StringBuilder(); 45 | char[] arr = new char[4096]; 46 | int am; 47 | while ((am = bf.read(arr)) != -1) { 48 | sb.append(arr, 0, am); 49 | } 50 | bf.close(); 51 | return sb.toString(); 52 | } 53 | } -------------------------------------------------------------------------------- /src/iris/json/test/SpeedTest.java: -------------------------------------------------------------------------------- 1 | package iris.json.test; 2 | 3 | import iris.json.IrisJsonItem; 4 | import iris.json.IrisJsonParser; 5 | import org.json.JSONObject; 6 | 7 | import java.io.IOException; 8 | 9 | /** 10 | * Создано 19.09.2020 11 | */ 12 | public class SpeedTest { 13 | public static void main(String[] args) throws IOException { 14 | /***** SPEED TEST ***************************/ 15 | 16 | var testString = UseTest.readText("test.json"); 17 | 18 | // Run 100_000 iterations to parse json-string with standars org.json parser 19 | testJsonParser(testString); 20 | 21 | // Run 100_000 iterations to parse json-string with Iris Json Parser 22 | //testIrisParser(testString); 23 | } 24 | 25 | private static void testIrisParser(String test) { 26 | System.gc(); 27 | for (var i = 1; i <= 1_000; i++) 28 | new IrisJsonParser(test).parse(); 29 | System.gc(); 30 | var start = System.currentTimeMillis(); 31 | IrisJsonItem rand; 32 | for (var i = 1; i <= 100_000; i++) { 33 | rand = new IrisJsonParser(test).parse(); 34 | //var d = rand.obj(); // uncomment it if you want to test full object tree build. Speed is still 30% better than standard JSON lib 35 | if (rand == null) // check for not to let compiler optimize code 36 | System.out.println("true"); 37 | } 38 | var end = System.currentTimeMillis(); 39 | System.out.println((end - start) + " ms"); 40 | } 41 | 42 | private static void testJsonParser(String test) { 43 | System.gc(); 44 | for (var i = 1; i <= 1_000; i++) 45 | JSONObject.stringToValue(test); 46 | System.gc(); 47 | var start = System.currentTimeMillis(); 48 | JSONObject rand; 49 | for (var i = 1; i <= 100_000; i++) { 50 | rand = new JSONObject(test); 51 | var d = rand; 52 | if (d == null) // check for not to let compiler optimize code 53 | System.out.println("true"); 54 | } 55 | var end = System.currentTimeMillis(); 56 | System.out.println((end - start) + " ms"); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/iris/json/IrisJsonString.java: -------------------------------------------------------------------------------- 1 | package iris.json; 2 | 3 | import java.io.IOException; 4 | import java.lang.Appendable; 5 | 6 | class IrisJsonString extends IrisJsonItem { 7 | 8 | private final IrisSequence data; 9 | 10 | public IrisJsonString(IrisSequence data) { 11 | super(IrisJson.Type.String); 12 | this.data = data; 13 | } 14 | 15 | @Override 16 | public A joinTo(A buffer) throws IOException { 17 | buffer.append('"'); 18 | data.joinTo(buffer); 19 | buffer.append('"'); 20 | return buffer; 21 | } 22 | 23 | @Override 24 | public IrisJsonItem get(int ind) { 25 | return IrisJsonNull.Null; 26 | } 27 | 28 | @Override 29 | public IrisJsonItem get(String key) { 30 | return IrisJsonNull.Null; 31 | } 32 | 33 | 34 | private String ready; 35 | 36 | private String getReady() { 37 | if (ready != null) 38 | return ready; 39 | var res = new StringBuilder(); 40 | var len = data.length(); 41 | var isEscape = false; 42 | var fromIndex = 0; 43 | var i = 0; 44 | do { 45 | var ch = data.charAt(i); 46 | if (isEscape) { 47 | isEscape = false; 48 | var repl = switch (ch) { 49 | case '"' -> '"'; 50 | case 'n' -> '\n'; 51 | case 'b' -> '\b'; 52 | case '/' -> '/'; 53 | case 'r' -> '\r'; 54 | case 't' -> '\t'; 55 | case 'u' -> 'u'; 56 | default -> '-'; 57 | }; 58 | if (ch != '-') { 59 | res.append(data, fromIndex, i - 1); 60 | if (repl == 'u') { 61 | var d = Integer.parseInt(data.subSequence(i + 1, i + 1 + 4).toString(), 16); 62 | res.appendCodePoint(d); 63 | i += 4; 64 | } else { 65 | res.append(repl); 66 | } 67 | fromIndex = i + 1; 68 | } 69 | } else { 70 | if (ch == '\\') 71 | isEscape = true; 72 | } 73 | i++; 74 | } while (i < len); 75 | 76 | 77 | if (fromIndex == 0) // no any escape 78 | ready = data.toString(); 79 | else { 80 | if (fromIndex != len) { 81 | res.append(data, fromIndex, len); 82 | } 83 | ready = res.toString(); 84 | } 85 | return ready; 86 | } 87 | 88 | 89 | public Object obj() { 90 | return getReady(); 91 | } 92 | } -------------------------------------------------------------------------------- /src/iris/json/IrisJsonObject.java: -------------------------------------------------------------------------------- 1 | package iris.json; 2 | 3 | import java.io.IOException; 4 | import java.lang.Appendable; 5 | import java.util.HashMap; 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | class IrisJsonObject extends IrisJsonItem { 10 | 11 | private final List entries; 12 | 13 | public IrisJsonObject(List entries) { 14 | super(IrisJson.Type.Object); 15 | this.entries = entries; 16 | } 17 | 18 | public static class Entry { 19 | 20 | private final IrisSequence key; 21 | private final IrisJsonItem value; 22 | 23 | public Entry(IrisSequence key, IrisJsonItem value) { 24 | this.key = key; 25 | this.value = value; 26 | } 27 | 28 | public String toString() { 29 | return "\"" + key + "\": " + value; 30 | } 31 | } 32 | 33 | @Override 34 | public A joinTo(A buffer) throws IOException { 35 | buffer.append("{"); 36 | var firstDone = false; 37 | for (Entry entry : entries) { 38 | if (firstDone) 39 | buffer.append(", "); 40 | else 41 | firstDone = true; 42 | buffer.append("\""); 43 | buffer.append(entry.key); 44 | buffer.append("\": "); 45 | entry.value.joinTo(buffer); 46 | 47 | } 48 | buffer.append('}'); 49 | return buffer; 50 | } 51 | 52 | @Override 53 | public IrisJsonItem get(int ind) { 54 | return get(String.valueOf(ind)); 55 | } 56 | 57 | @Override 58 | public IrisJsonItem get(String key) { 59 | var res = getMap().get(key); 60 | if (res == null) 61 | return IrisJsonNull.Null; 62 | else 63 | return res; 64 | } 65 | 66 | private HashMap map; 67 | //by lazy(LazyThreadSafetyMode.NONE) { init() } 68 | 69 | private Map getMap() { 70 | if (map != null) 71 | return map; 72 | var t = new HashMap(); 73 | for (var it : entries) { 74 | t.put(it.key.toString(), it.value); 75 | } 76 | return map = t; 77 | } 78 | 79 | private Object obj; 80 | 81 | @Override 82 | public Object obj() { 83 | if (obj != null) 84 | return obj; 85 | var res = new HashMap(); 86 | for (var it : getMap().entrySet()) 87 | res.put(it.getKey(), it.getValue().obj()); 88 | obj = res; 89 | return res; 90 | } 91 | } -------------------------------------------------------------------------------- /src/iris/json/IrisJsonItem.java: -------------------------------------------------------------------------------- 1 | package iris.json; 2 | 3 | import java.io.IOException; 4 | import java.lang.Appendable; 5 | import java.util.ArrayList; 6 | import java.util.Collection; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | public abstract class IrisJsonItem { 11 | 12 | protected IrisJson.Type type; 13 | 14 | public IrisJsonItem(IrisJson.Type type) { 15 | this.type = type; 16 | } 17 | 18 | public abstract IrisJsonItem get(int ind); 19 | public abstract IrisJsonItem get(String key); 20 | public abstract Object obj(); 21 | public abstract A joinTo(A buffer) throws IOException; 22 | 23 | public String toString() { 24 | try { 25 | return joinTo(new StringBuilder()).toString(); 26 | } catch (IOException e) { 27 | return null; 28 | } 29 | } 30 | 31 | public Integer asIntOrNull() { 32 | var obj = obj(); 33 | return obj == null? null : ((Number)obj).intValue(); 34 | } 35 | 36 | public Integer asInt() { 37 | return ((Number)obj()).intValue(); 38 | } 39 | 40 | public Long asLongOrNull() { 41 | var obj = obj(); 42 | return obj == null? null : ((Number)obj).longValue(); 43 | } 44 | 45 | public Long asLong() { 46 | return ((Number)obj()).longValue(); 47 | } 48 | 49 | public Double asDoubleOrNull() { 50 | var obj = obj(); 51 | return obj == null? null : ((Number)obj).doubleValue(); 52 | } 53 | 54 | public Double asDouble() { 55 | return ((Number)obj()).doubleValue(); 56 | } 57 | 58 | public Float asFloatOrNull() { 59 | var obj = obj(); 60 | return obj == null? null : ((Number)obj).floatValue(); 61 | } 62 | 63 | public Float asFloat() { 64 | return ((Number)obj()).floatValue(); 65 | } 66 | 67 | public Boolean asBooleanOrNull() { 68 | var obj = obj(); 69 | return obj == null? null : (boolean)obj; 70 | } 71 | 72 | public Boolean asBoolean() { 73 | return (boolean)obj(); 74 | } 75 | 76 | public List asListOrNull() { 77 | var obj = obj(); 78 | if (!(obj instanceof Collection)) 79 | return null; 80 | if (obj instanceof List) 81 | return (List)obj; 82 | return new ArrayList<>((Collection)obj); 83 | } 84 | 85 | public List asList() { 86 | var obj = obj(); 87 | if (obj instanceof List) 88 | return (List)obj; 89 | return new ArrayList<>((Collection)obj); 90 | } 91 | 92 | public Map asMapOrNull() { 93 | var obj = obj(); 94 | return obj == null? null : (Map)obj; 95 | } 96 | 97 | public Map asMap() { 98 | return (Map)obj(); 99 | } 100 | 101 | public IrisJsonItem find(String[] tree) { 102 | var cur = this; 103 | for (var t : tree) 104 | cur = cur.get(t); 105 | return cur; 106 | } 107 | 108 | public IrisJsonItem find(List tree) { 109 | var cur = this; 110 | for (var t : tree) 111 | cur = cur.get(t); 112 | return cur; 113 | } 114 | 115 | public IrisJsonItem find(String tree) { 116 | return find(tree.replace('[', '.').replace("]", "").replace(' ', '.').split("\\.")); 117 | } 118 | 119 | } -------------------------------------------------------------------------------- /test.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "message_new", 3 | "object": { 4 | "message": { 5 | "date": 1586893276, 6 | "from_id": 23434235, 7 | "id": 0, 8 | "out": 0, 9 | "peer_id": 2000000001, 10 | "text": "", 11 | "conversation_message_id": 21248, 12 | "fwd_messages": [], 13 | "ref": "feed_top", 14 | "ref_source": "feed_top", 15 | "important": false, 16 | "random_id": 0, 17 | "attachments": [ 18 | { 19 | "type": "wall", 20 | "wall": { 21 | "id": 125041, 22 | "from_id": -34534534, 23 | "to_id": -34534534, 24 | "date": 1586892602, 25 | "post_type": "post", 26 | "text": "«Левада-центр» отметил в 2017 году 42% людей, а в настоящее время всего 29%.\n\n#OL #Статистика", 27 | "marked_as_ads": 0, 28 | "attachments": [ 29 | { 30 | "type": "photo", 31 | "photo": { 32 | "id": 443355, 33 | "album_id": -7, 34 | "owner_id": -444444, 35 | "user_id": 100, 36 | "sizes": [ 37 | { 38 | "type": "m", 39 | "url": "https://sun9-47.userapi.com/c856524/v856524072/15f33d/JF4hwJsQXyI.jpg", 40 | "width": 130, 41 | "height": 87 42 | }, 43 | { 44 | "type": "o", 45 | "url": "https://sun9-43.userapi.com/c856524/v856524072/15f340/RPBGus8_iOM.jpg", 46 | "width": 130, 47 | "height": 87 48 | }, 49 | { 50 | "type": "p", 51 | "url": "https://sun9-50.userapi.com/c856524/v856524072/15f341/7ESDnWLgbWM.jpg", 52 | "width": 200, 53 | "height": 133 54 | }, 55 | { 56 | "type": "q", 57 | "url": "https://sun9-32.userapi.com/c856524/v856524072/15f342/3A1Tkaj-ZLQ.jpg", 58 | "width": 320, 59 | "height": 213 60 | }, 61 | { 62 | "type": "r", 63 | "url": "https://sun9-38.userapi.com/c856524/v856524072/15f343/e-DhriFCu6c.jpg", 64 | "width": 510, 65 | "height": 340 66 | }, 67 | { 68 | "type": "s", 69 | "url": "https://sun9-67.userapi.com/c856524/v856524072/15f33c/tZfn9jsYSQM.jpg", 70 | "width": 75, 71 | "height": 50 72 | }, 73 | { 74 | "type": "x", 75 | "url": "https://sun9-52.userapi.com/c856524/v856524072/15f33e/PrZ3kQX91AE.jpg", 76 | "width": 604, 77 | "height": 403 78 | }, 79 | { 80 | "type": "y", 81 | "url": "https://sun9-43.userapi.com/c856524/v856524072/15f33f/elXIIDh7RwY.jpg", 82 | "width": 630, 83 | "height": 420 84 | } 85 | ], 86 | "text": "", 87 | "date": 1586889483, 88 | "access_key": "2cdc6a54ad629248b0" 89 | } 90 | }, 91 | { 92 | "type": "poll", 93 | "poll": { 94 | "id": 370300936, 95 | "owner_id": -133353, 96 | "created": 1586889258, 97 | "question": "Should you try?", 98 | "votes": 283, 99 | "answers": [ 100 | { 101 | "id": 1237881034, 102 | "text": "Да", 103 | "votes": 35, 104 | "rate": 12.37 105 | }, 106 | { 107 | "id": 1237881035, 108 | "text": "Нет", 109 | "votes": 248, 110 | "rate": 87.63 111 | } 112 | ], 113 | "anonymous": true, 114 | "multiple": false, 115 | "answer_ids": [], 116 | "end_date": 0, 117 | "closed": false, 118 | "is_board": false, 119 | "can_edit": false, 120 | "can_vote": false, 121 | "can_report": false, 122 | "can_share": false, 123 | "author_id": -173961453, 124 | "background": { 125 | "angle": 180, 126 | "color": "5c6778", 127 | "id": 8, 128 | "name": "серый фон", 129 | "points": [ 130 | { 131 | "color": "707c8c", 132 | "position": 0 133 | }, 134 | { 135 | "color": "4d5565", 136 | "position": 1 137 | } 138 | ], 139 | "type": "gradient" 140 | } 141 | } 142 | } 143 | ], 144 | "comments": { 145 | "count": 7 146 | }, 147 | "likes": { 148 | "count": 12 149 | }, 150 | "reposts": { 151 | "count": 0 152 | }, 153 | "views": { 154 | "count": 607 155 | }, 156 | "from": { 157 | "id": 173961453, 158 | "name": "Lalala OL", 159 | "screen_name": "efefe", 160 | "is_closed": 0, 161 | "type": "page" 162 | } 163 | } 164 | } 165 | ], 166 | "is_hidden": false 167 | }, 168 | "client_info": { 169 | "button_actions": [ 170 | "text", 171 | "vkpay", 172 | "open_app", 173 | "location", 174 | "open_link" 175 | ], 176 | "keyboard": true, 177 | "inline_keyboard": true, 178 | "lang_id": 0 179 | } 180 | }, 181 | "group_id": 181050268, 182 | "event_id": "345g3434gfdgdfgg34g34g34g", 183 | "secret": "34g34g34g34ggrdfdgfd" 184 | } -------------------------------------------------------------------------------- /src/iris/json/IrisJsonParser.java: -------------------------------------------------------------------------------- 1 | package iris.json; 2 | 3 | import java.util.ArrayList; 4 | 5 | public class IrisJsonParser { 6 | 7 | private final String array; 8 | private int counter = 0; 9 | 10 | public IrisJsonParser(String text) { 11 | this.array = text; 12 | } 13 | 14 | public IrisJsonItem parse() { 15 | return readItem(); 16 | } 17 | 18 | private IrisJsonItem readItem() { 19 | skipWhitespaces(); 20 | var ch = array.charAt(counter++); 21 | IrisJson.Type type; 22 | if (Character.isDigit(ch) || Character.isLetter(ch) || ch == '-') 23 | type = IrisJson.Type.Value; 24 | else if (ch == '{') 25 | type = IrisJson.Type.Object; 26 | else if (ch == '[') 27 | type = IrisJson.Type.Array; 28 | else if (ch == '"') 29 | type = IrisJson.Type.String; 30 | else 31 | throw new IllegalArgumentException("Character: \"$char\" at $counter\n" + getPlace()); 32 | 33 | if (type == IrisJson.Type.Value) { // примитивы 34 | counter--; 35 | var start = counter; 36 | var value = readPrimitive(); 37 | var end = counter; 38 | return new IrisJsonValue(new IrisSequence(array, start, end), value); 39 | } else if (type == IrisJson.Type.Object) { 40 | return readObject(); 41 | } else if (type == IrisJson.Type.String) { 42 | var start = counter; 43 | readString(); 44 | var end = counter - 1; 45 | //counter++; // поправка, т.к. мы вышли из строки, узнав про кавычку. на неё и двигаемся 46 | return new IrisJsonString(new IrisSequence(array, start, end)); 47 | } else if (type == IrisJson.Type.Array) { 48 | return readArray(); 49 | } else 50 | throw new IllegalArgumentException(type + " not realised yet " + counter + "\n" + getPlace()); 51 | } 52 | 53 | private String getPlace() { 54 | return '"' + array.substring(Math.max(0, counter - 10), Math.min(counter + 10, array.length() - 1))+'"'; 55 | } 56 | 57 | private IrisJsonObject readObject() { 58 | final var entries = new ArrayList(); 59 | final var len = array.length(); 60 | do { 61 | skipWhitespaces(); 62 | // "id" : ... 63 | var ch = array.charAt(counter++); 64 | if (ch == '}') 65 | break; 66 | if (ch == ',') { 67 | skipWhitespaces(); 68 | ch = array.charAt(counter++); 69 | } 70 | if (ch != '"') 71 | throw new IllegalArgumentException("\" (quote) was expected in position $counter\n" + getPlace()); 72 | 73 | var start = counter; 74 | // ключ 75 | readString(); 76 | var end = counter - 1; 77 | var key = new IrisSequence(array, start, end); 78 | skipWhitespaces(); 79 | ch = array.charAt(counter++); 80 | if (ch != ':') 81 | throw new IllegalArgumentException("\":\" was expected in position $counter\n" + getPlace()); 82 | skipWhitespaces(); 83 | var value = readItem(); 84 | entries.add(new IrisJsonObject.Entry(key, value)); 85 | //counter-- 86 | } while (counter < len); 87 | return new IrisJsonObject(entries); 88 | } 89 | 90 | private IrisJsonArray readArray() { 91 | var entries = new ArrayList(); 92 | final var len = array.length(); 93 | do { 94 | skipWhitespaces(); 95 | // "id" : ... 96 | var ch = array.charAt(counter++); 97 | if (ch == ']') 98 | break; 99 | if (ch == ',') { 100 | skipWhitespaces(); 101 | } else 102 | counter--; 103 | 104 | var value = readItem(); 105 | entries.add(value); 106 | skipWhitespaces(); 107 | ch = array.charAt(counter); 108 | if (ch == ']') { 109 | counter++; 110 | break; 111 | } 112 | } while (counter < len); 113 | return new IrisJsonArray(entries); 114 | } 115 | 116 | private void skipWhitespaces() { 117 | final var len = array.length(); 118 | do { 119 | var ch = array.charAt(counter); 120 | if (!Character.isWhitespace(ch)) { 121 | break; 122 | } 123 | counter++; 124 | } while (counter < len); 125 | } 126 | 127 | private void readString() { 128 | var escaping = false; 129 | final var len = array.length(); 130 | do { 131 | var ch = array.charAt(counter++); 132 | if (ch == '\\') 133 | escaping = true; 134 | else if (escaping) { 135 | escaping = false; 136 | } else if (ch == '"') { 137 | break; 138 | } 139 | } while (counter < len); 140 | } 141 | 142 | private IrisJson.ValueType readPrimitive() { 143 | var curType = IrisJson.ValueType.Integer; 144 | final var first = counter; 145 | final var len = array.length(); 146 | do { 147 | final var ch = array.charAt(counter); 148 | if (Character.isDigit(ch)) {} 149 | else if (ch == '-') { 150 | if (first != counter) curType = IrisJson.ValueType.Constant; 151 | } else if (ch == '.') { 152 | if (curType == IrisJson.ValueType.Integer) curType = IrisJson.ValueType.Float; 153 | } else if (Character.isLetter(ch)) { 154 | curType = IrisJson.ValueType.Constant; 155 | } else 156 | break; 157 | counter++; 158 | } while (counter < len); 159 | return curType; 160 | } 161 | } --------------------------------------------------------------------------------