├── 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 | }
--------------------------------------------------------------------------------