Mercurial Hosting > luan
annotate src/goodjava/json/JsonParser.java @ 1495:2e8a5df45d56
better xml
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Tue, 05 May 2020 11:03:48 -0600 |
parents | 27efb1fcbcb5 |
children |
rev | line source |
---|---|
1402
27efb1fcbcb5
move luan.lib to goodjava
Franklin Schmidt <fschmidt@gmail.com>
parents:
1307
diff
changeset
|
1 package goodjava.json; |
720 | 2 |
3 import java.util.List; | |
4 import java.util.ArrayList; | |
5 import java.util.Map; | |
6 import java.util.LinkedHashMap; | |
1112 | 7 import java.util.Collections; |
1402
27efb1fcbcb5
move luan.lib to goodjava
Franklin Schmidt <fschmidt@gmail.com>
parents:
1307
diff
changeset
|
8 import goodjava.parser.Parser; |
27efb1fcbcb5
move luan.lib to goodjava
Franklin Schmidt <fschmidt@gmail.com>
parents:
1307
diff
changeset
|
9 import goodjava.parser.ParseException; |
720 | 10 |
11 | |
1112 | 12 public final class JsonParser { |
720 | 13 |
14 public static Object parse(String text) throws ParseException { | |
1112 | 15 return new JsonParser(text).parse(); |
720 | 16 } |
17 | |
18 private final Parser parser; | |
19 | |
1112 | 20 private JsonParser(String text) { |
720 | 21 this.parser = new Parser(text); |
22 } | |
23 | |
24 private ParseException exception(String msg) { | |
25 return new ParseException(parser,msg); | |
26 } | |
27 | |
28 private Object parse() throws ParseException { | |
29 spaces(); | |
30 Object value = value(); | |
31 spaces(); | |
32 if( !parser.endOfInput() ) | |
33 throw exception("unexpected text"); | |
34 return value; | |
35 } | |
36 | |
37 private Object value() throws ParseException { | |
38 if( parser.match("null") ) | |
39 return null; | |
40 if( parser.match("true") ) | |
41 return Boolean.TRUE; | |
42 if( parser.match("false") ) | |
43 return Boolean.FALSE; | |
44 String s = string(); | |
45 if( s != null ) | |
46 return s; | |
47 Number n = number(); | |
48 if( n != null ) | |
49 return n; | |
1112 | 50 List a = array(); |
720 | 51 if( a != null ) |
52 return a; | |
1112 | 53 Map o = object(); |
720 | 54 if( o != null ) |
55 return o; | |
56 throw exception("invalid value"); | |
57 } | |
58 | |
59 private String string() throws ParseException { | |
60 parser.begin(); | |
61 if( !parser.match('"') ) | |
62 return parser.failure(null); | |
63 StringBuilder sb = new StringBuilder(); | |
64 while( parser.anyChar() ) { | |
65 char c = parser.lastChar(); | |
66 switch(c) { | |
67 case '"': | |
68 return parser.success(sb.toString()); | |
69 case '\\': | |
70 if( parser.anyChar() ) { | |
71 c = parser.lastChar(); | |
72 switch(c) { | |
73 case '"': | |
1307 | 74 case '\'': // not in spec |
720 | 75 case '\\': |
76 case '/': | |
77 sb.append(c); | |
78 continue; | |
79 case 'b': | |
80 sb.append('\b'); | |
81 continue; | |
82 case 'f': | |
83 sb.append('\f'); | |
84 continue; | |
85 case 'n': | |
86 sb.append('\n'); | |
87 continue; | |
88 case 'r': | |
89 sb.append('\r'); | |
90 continue; | |
91 case 't': | |
92 sb.append('\t'); | |
93 continue; | |
94 case 'u': | |
95 int n = 0; | |
96 for( int i=0; i<4; i++ ) { | |
97 int d; | |
98 if( parser.inCharRange('0','9') ) { | |
99 d = parser.lastChar() - '0'; | |
100 } else if( parser.inCharRange('a','f') ) { | |
101 d = parser.lastChar() - 'a' + 10; | |
102 } else if( parser.inCharRange('A','F') ) { | |
103 d = parser.lastChar() - 'A' + 10; | |
104 } else { | |
105 throw exception("invalid hex digit"); | |
106 } | |
107 n = 16*n + d; | |
108 } | |
109 sb.append((char)n); | |
110 continue; | |
111 } | |
112 } | |
113 throw exception("invalid escape char"); | |
114 default: | |
115 sb.append(c); | |
116 } | |
117 } | |
118 parser.failure(); | |
119 throw exception("unclosed string"); | |
120 } | |
121 | |
122 private Number number() { | |
123 int start = parser.begin(); | |
759
ae612dfc57cb
better handling of longs in rpc and json
Franklin Schmidt <fschmidt@gmail.com>
parents:
758
diff
changeset
|
124 boolean isFloat = false; |
720 | 125 parser.match('-'); |
126 if( !parser.match('0') ) { | |
127 if( !parser.inCharRange('1','9') ) | |
128 return parser.failure(null); | |
129 while( parser.inCharRange('0','9') ); | |
130 } | |
131 if( parser.match('.') ) { | |
132 if( !parser.inCharRange('0','9') ) | |
133 return parser.failure(null); | |
134 while( parser.inCharRange('0','9') ); | |
759
ae612dfc57cb
better handling of longs in rpc and json
Franklin Schmidt <fschmidt@gmail.com>
parents:
758
diff
changeset
|
135 isFloat = true; |
720 | 136 } |
137 if( parser.anyOf("eE") ) { | |
138 parser.anyOf("+-"); | |
139 if( !parser.inCharRange('0','9') ) | |
140 return parser.failure(null); | |
141 while( parser.inCharRange('0','9') ); | |
759
ae612dfc57cb
better handling of longs in rpc and json
Franklin Schmidt <fschmidt@gmail.com>
parents:
758
diff
changeset
|
142 isFloat = true; |
720 | 143 } |
144 String s = parser.textFrom(start); | |
759
ae612dfc57cb
better handling of longs in rpc and json
Franklin Schmidt <fschmidt@gmail.com>
parents:
758
diff
changeset
|
145 Number n; |
ae612dfc57cb
better handling of longs in rpc and json
Franklin Schmidt <fschmidt@gmail.com>
parents:
758
diff
changeset
|
146 if(isFloat) |
ae612dfc57cb
better handling of longs in rpc and json
Franklin Schmidt <fschmidt@gmail.com>
parents:
758
diff
changeset
|
147 n = Double.valueOf(s); |
ae612dfc57cb
better handling of longs in rpc and json
Franklin Schmidt <fschmidt@gmail.com>
parents:
758
diff
changeset
|
148 else |
ae612dfc57cb
better handling of longs in rpc and json
Franklin Schmidt <fschmidt@gmail.com>
parents:
758
diff
changeset
|
149 n = Long.valueOf(s); |
ae612dfc57cb
better handling of longs in rpc and json
Franklin Schmidt <fschmidt@gmail.com>
parents:
758
diff
changeset
|
150 return parser.success(n); |
720 | 151 } |
152 | |
1112 | 153 private List array() throws ParseException { |
720 | 154 parser.begin(); |
155 if( !parser.match('[') ) | |
156 return parser.failure(null); | |
157 spaces(); | |
158 if( parser.match(']') ) | |
1112 | 159 return parser.success(Collections.emptyList()); |
720 | 160 List list = new ArrayList(); |
161 list.add( value() ); | |
162 spaces(); | |
163 while( parser.match(',') ) { | |
164 spaces(); | |
165 list.add( value() ); | |
166 spaces(); | |
167 } | |
168 if( parser.match(']') ) | |
1112 | 169 return parser.success(list); |
720 | 170 if( parser.endOfInput() ) { |
171 parser.failure(); | |
172 throw exception("unclosed array"); | |
173 } | |
174 throw exception("unexpected text in array"); | |
175 } | |
176 | |
1112 | 177 private Map object() throws ParseException { |
720 | 178 parser.begin(); |
179 if( !parser.match('{') ) | |
180 return parser.failure(null); | |
181 spaces(); | |
182 if( parser.match('}') ) | |
1112 | 183 return parser.success(Collections.emptyMap()); |
720 | 184 Map map = new LinkedHashMap(); |
185 addEntry(map); | |
186 while( parser.match(',') ) { | |
187 spaces(); | |
188 addEntry(map); | |
189 } | |
190 if( parser.match('}') ) | |
1112 | 191 return parser.success(map); |
720 | 192 if( parser.endOfInput() ) { |
193 parser.failure(); | |
194 throw exception("unclosed object"); | |
195 } | |
196 throw exception("unexpected text in object"); | |
197 } | |
198 | |
199 private void addEntry(Map map) throws ParseException { | |
200 String key = string(); | |
201 if( key==null ) | |
202 throw exception("invalid object key"); | |
203 spaces(); | |
204 if( !parser.match(':') ) | |
205 throw exception("':' expected"); | |
206 spaces(); | |
207 Object value = value(); | |
208 spaces(); | |
209 map.put(key,value); | |
210 } | |
211 | |
212 private void spaces() { | |
213 while( parser.anyOf(" \t\r\n") ); | |
214 } | |
758
c29d11d675fd
added Json.toString() and rpc now sends tables as json
Franklin Schmidt <fschmidt@gmail.com>
parents:
720
diff
changeset
|
215 |
720 | 216 } |