root / src / org / json / JSONWriter.java @ 4bf05cad
History | View | Annotate | Download (10 kB)
1 | 14ad7326 | pastith | package org.json; |
---|---|---|---|
2 | 14ad7326 | pastith | |
3 | 14ad7326 | pastith | import java.io.IOException; |
4 | 14ad7326 | pastith | import java.io.Writer; |
5 | 14ad7326 | pastith | |
6 | 14ad7326 | pastith | /*
|
7 | 14ad7326 | pastith | Copyright (c) 2006 JSON.org
|
8 | 14ad7326 | pastith | |
9 | 14ad7326 | pastith | Permission is hereby granted, free of charge, to any person obtaining a copy
|
10 | 14ad7326 | pastith | of this software and associated documentation files (the "Software"), to deal
|
11 | 14ad7326 | pastith | in the Software without restriction, including without limitation the rights
|
12 | 14ad7326 | pastith | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
13 | 14ad7326 | pastith | copies of the Software, and to permit persons to whom the Software is
|
14 | 14ad7326 | pastith | furnished to do so, subject to the following conditions:
|
15 | 14ad7326 | pastith | |
16 | 14ad7326 | pastith | The above copyright notice and this permission notice shall be included in all
|
17 | 14ad7326 | pastith | copies or substantial portions of the Software.
|
18 | 14ad7326 | pastith | |
19 | 14ad7326 | pastith | The Software shall be used for Good, not Evil.
|
20 | 14ad7326 | pastith | |
21 | 14ad7326 | pastith | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
22 | 14ad7326 | pastith | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
23 | 14ad7326 | pastith | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
24 | 14ad7326 | pastith | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
25 | 14ad7326 | pastith | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
26 | 14ad7326 | pastith | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
27 | 14ad7326 | pastith | SOFTWARE.
|
28 | 14ad7326 | pastith | */
|
29 | 14ad7326 | pastith | |
30 | 14ad7326 | pastith | /**
|
31 | 14ad7326 | pastith | * JSONWriter provides a quick and convenient way of producing JSON text.
|
32 | 14ad7326 | pastith | * The texts produced strictly conform to JSON syntax rules. No whitespace is
|
33 | 14ad7326 | pastith | * added, so the results are ready for transmission or storage. Each instance of
|
34 | 14ad7326 | pastith | * JSONWriter can produce one JSON text.
|
35 | 14ad7326 | pastith | * <p>
|
36 | 14ad7326 | pastith | * A JSONWriter instance provides a <code>value</code> method for appending
|
37 | 14ad7326 | pastith | * values to the
|
38 | 14ad7326 | pastith | * text, and a <code>key</code>
|
39 | 14ad7326 | pastith | * method for adding keys before values in objects. There are <code>array</code>
|
40 | 14ad7326 | pastith | * and <code>endArray</code> methods that make and bound array values, and
|
41 | 14ad7326 | pastith | * <code>object</code> and <code>endObject</code> methods which make and bound
|
42 | 14ad7326 | pastith | * object values. All of these methods return the JSONWriter instance,
|
43 | 14ad7326 | pastith | * permitting a cascade style. For example, <pre>
|
44 | 14ad7326 | pastith | * new JSONWriter(myWriter)
|
45 | 14ad7326 | pastith | * .object()
|
46 | 14ad7326 | pastith | * .key("JSON")
|
47 | 14ad7326 | pastith | * .value("Hello, World!")
|
48 | 14ad7326 | pastith | * .endObject();</pre> which writes <pre>
|
49 | 14ad7326 | pastith | * {"JSON":"Hello, World!"}</pre>
|
50 | 14ad7326 | pastith | * <p>
|
51 | 14ad7326 | pastith | * The first method called must be <code>array</code> or <code>object</code>.
|
52 | 14ad7326 | pastith | * There are no methods for adding commas or colons. JSONWriter adds them for
|
53 | 14ad7326 | pastith | * you. Objects and arrays can be nested up to 20 levels deep.
|
54 | 14ad7326 | pastith | * <p>
|
55 | 14ad7326 | pastith | * This can sometimes be easier than using a JSONObject to build a string.
|
56 | 14ad7326 | pastith | * @author JSON.org
|
57 | 14ad7326 | pastith | * @version 2008-09-18
|
58 | 14ad7326 | pastith | */
|
59 | 14ad7326 | pastith | public class JSONWriter { |
60 | 14ad7326 | pastith | private static final int maxdepth = 20; |
61 | 14ad7326 | pastith | |
62 | 14ad7326 | pastith | /**
|
63 | 14ad7326 | pastith | * The comma flag determines if a comma should be output before the next
|
64 | 14ad7326 | pastith | * value.
|
65 | 14ad7326 | pastith | */
|
66 | 14ad7326 | pastith | private boolean comma; |
67 | 14ad7326 | pastith | |
68 | 14ad7326 | pastith | /**
|
69 | 14ad7326 | pastith | * The current mode. Values:
|
70 | 14ad7326 | pastith | * 'a' (array),
|
71 | 14ad7326 | pastith | * 'd' (done),
|
72 | 14ad7326 | pastith | * 'i' (initial),
|
73 | 14ad7326 | pastith | * 'k' (key),
|
74 | 14ad7326 | pastith | * 'o' (object).
|
75 | 14ad7326 | pastith | */
|
76 | 14ad7326 | pastith | protected char mode; |
77 | 14ad7326 | pastith | |
78 | 14ad7326 | pastith | /**
|
79 | 14ad7326 | pastith | * The object/array stack.
|
80 | 14ad7326 | pastith | */
|
81 | 14ad7326 | pastith | private JSONObject stack[]; |
82 | 14ad7326 | pastith | |
83 | 14ad7326 | pastith | /**
|
84 | 14ad7326 | pastith | * The stack top index. A value of 0 indicates that the stack is empty.
|
85 | 14ad7326 | pastith | */
|
86 | 14ad7326 | pastith | private int top; |
87 | 14ad7326 | pastith | |
88 | 14ad7326 | pastith | /**
|
89 | 14ad7326 | pastith | * The writer that will receive the output.
|
90 | 14ad7326 | pastith | */
|
91 | 14ad7326 | pastith | protected Writer writer; |
92 | 14ad7326 | pastith | |
93 | 14ad7326 | pastith | /**
|
94 | 14ad7326 | pastith | * Make a fresh JSONWriter. It can be used to build one JSON text.
|
95 | 14ad7326 | pastith | */
|
96 | 14ad7326 | pastith | public JSONWriter(Writer w) { |
97 | 14ad7326 | pastith | this.comma = false; |
98 | 14ad7326 | pastith | this.mode = 'i'; |
99 | 14ad7326 | pastith | this.stack = new JSONObject[maxdepth]; |
100 | 14ad7326 | pastith | this.top = 0; |
101 | 14ad7326 | pastith | this.writer = w;
|
102 | 14ad7326 | pastith | } |
103 | 14ad7326 | pastith | |
104 | 14ad7326 | pastith | /**
|
105 | 14ad7326 | pastith | * Append a value.
|
106 | 14ad7326 | pastith | * @param s A string value.
|
107 | 14ad7326 | pastith | * @return this
|
108 | 14ad7326 | pastith | * @throws JSONException If the value is out of sequence.
|
109 | 14ad7326 | pastith | */
|
110 | 14ad7326 | pastith | private JSONWriter append(String s) throws JSONException { |
111 | 14ad7326 | pastith | if (s == null) { |
112 | 14ad7326 | pastith | throw new JSONException("Null pointer"); |
113 | 14ad7326 | pastith | } |
114 | 14ad7326 | pastith | if (this.mode == 'o' || this.mode == 'a') { |
115 | 14ad7326 | pastith | try {
|
116 | 14ad7326 | pastith | if (this.comma && this.mode == 'a') { |
117 | 14ad7326 | pastith | this.writer.write(','); |
118 | 14ad7326 | pastith | } |
119 | 14ad7326 | pastith | this.writer.write(s);
|
120 | 14ad7326 | pastith | } catch (IOException e) { |
121 | 14ad7326 | pastith | throw new JSONException(e); |
122 | 14ad7326 | pastith | } |
123 | 14ad7326 | pastith | if (this.mode == 'o') { |
124 | 14ad7326 | pastith | this.mode = 'k'; |
125 | 14ad7326 | pastith | } |
126 | 14ad7326 | pastith | this.comma = true; |
127 | 14ad7326 | pastith | return this; |
128 | 14ad7326 | pastith | } |
129 | 14ad7326 | pastith | throw new JSONException("Value out of sequence."); |
130 | 14ad7326 | pastith | } |
131 | 14ad7326 | pastith | |
132 | 14ad7326 | pastith | /**
|
133 | 14ad7326 | pastith | * Begin appending a new array. All values until the balancing
|
134 | 14ad7326 | pastith | * <code>endArray</code> will be appended to this array. The
|
135 | 14ad7326 | pastith | * <code>endArray</code> method must be called to mark the array's end.
|
136 | 14ad7326 | pastith | * @return this
|
137 | 14ad7326 | pastith | * @throws JSONException If the nesting is too deep, or if the object is
|
138 | 14ad7326 | pastith | * started in the wrong place (for example as a key or after the end of the
|
139 | 14ad7326 | pastith | * outermost array or object).
|
140 | 14ad7326 | pastith | */
|
141 | 14ad7326 | pastith | public JSONWriter array() throws JSONException { |
142 | 14ad7326 | pastith | if (this.mode == 'i' || this.mode == 'o' || this.mode == 'a') { |
143 | 14ad7326 | pastith | this.push(null); |
144 | 14ad7326 | pastith | this.append("["); |
145 | 14ad7326 | pastith | this.comma = false; |
146 | 14ad7326 | pastith | return this; |
147 | 14ad7326 | pastith | } |
148 | 14ad7326 | pastith | throw new JSONException("Misplaced array."); |
149 | 14ad7326 | pastith | } |
150 | 14ad7326 | pastith | |
151 | 14ad7326 | pastith | /**
|
152 | 14ad7326 | pastith | * End something.
|
153 | 14ad7326 | pastith | * @param m Mode
|
154 | 14ad7326 | pastith | * @param c Closing character
|
155 | 14ad7326 | pastith | * @return this
|
156 | 14ad7326 | pastith | * @throws JSONException If unbalanced.
|
157 | 14ad7326 | pastith | */
|
158 | 14ad7326 | pastith | private JSONWriter end(char m, char c) throws JSONException { |
159 | 14ad7326 | pastith | if (this.mode != m) { |
160 | 14ad7326 | pastith | throw new JSONException(m == 'o' ? "Misplaced endObject." : |
161 | 14ad7326 | pastith | "Misplaced endArray.");
|
162 | 14ad7326 | pastith | } |
163 | 14ad7326 | pastith | this.pop(m);
|
164 | 14ad7326 | pastith | try {
|
165 | 14ad7326 | pastith | this.writer.write(c);
|
166 | 14ad7326 | pastith | } catch (IOException e) { |
167 | 14ad7326 | pastith | throw new JSONException(e); |
168 | 14ad7326 | pastith | } |
169 | 14ad7326 | pastith | this.comma = true; |
170 | 14ad7326 | pastith | return this; |
171 | 14ad7326 | pastith | } |
172 | 14ad7326 | pastith | |
173 | 14ad7326 | pastith | /**
|
174 | 14ad7326 | pastith | * End an array. This method most be called to balance calls to
|
175 | 14ad7326 | pastith | * <code>array</code>.
|
176 | 14ad7326 | pastith | * @return this
|
177 | 14ad7326 | pastith | * @throws JSONException If incorrectly nested.
|
178 | 14ad7326 | pastith | */
|
179 | 14ad7326 | pastith | public JSONWriter endArray() throws JSONException { |
180 | 14ad7326 | pastith | return this.end('a', ']'); |
181 | 14ad7326 | pastith | } |
182 | 14ad7326 | pastith | |
183 | 14ad7326 | pastith | /**
|
184 | 14ad7326 | pastith | * End an object. This method most be called to balance calls to
|
185 | 14ad7326 | pastith | * <code>object</code>.
|
186 | 14ad7326 | pastith | * @return this
|
187 | 14ad7326 | pastith | * @throws JSONException If incorrectly nested.
|
188 | 14ad7326 | pastith | */
|
189 | 14ad7326 | pastith | public JSONWriter endObject() throws JSONException { |
190 | 14ad7326 | pastith | return this.end('k', '}'); |
191 | 14ad7326 | pastith | } |
192 | 14ad7326 | pastith | |
193 | 14ad7326 | pastith | /**
|
194 | 14ad7326 | pastith | * Append a key. The key will be associated with the next value. In an
|
195 | 14ad7326 | pastith | * object, every value must be preceded by a key.
|
196 | 14ad7326 | pastith | * @param s A key string.
|
197 | 14ad7326 | pastith | * @return this
|
198 | 14ad7326 | pastith | * @throws JSONException If the key is out of place. For example, keys
|
199 | 14ad7326 | pastith | * do not belong in arrays or if the key is null.
|
200 | 14ad7326 | pastith | */
|
201 | 14ad7326 | pastith | public JSONWriter key(String s) throws JSONException { |
202 | 14ad7326 | pastith | if (s == null) { |
203 | 14ad7326 | pastith | throw new JSONException("Null key."); |
204 | 14ad7326 | pastith | } |
205 | 14ad7326 | pastith | if (this.mode == 'k') { |
206 | 14ad7326 | pastith | try {
|
207 | 14ad7326 | pastith | if (this.comma) { |
208 | 14ad7326 | pastith | this.writer.write(','); |
209 | 14ad7326 | pastith | } |
210 | 14ad7326 | pastith | stack[top - 1].putOnce(s, Boolean.TRUE); |
211 | 14ad7326 | pastith | this.writer.write(JSONObject.quote(s));
|
212 | 14ad7326 | pastith | this.writer.write(':'); |
213 | 14ad7326 | pastith | this.comma = false; |
214 | 14ad7326 | pastith | this.mode = 'o'; |
215 | 14ad7326 | pastith | return this; |
216 | 14ad7326 | pastith | } catch (IOException e) { |
217 | 14ad7326 | pastith | throw new JSONException(e); |
218 | 14ad7326 | pastith | } |
219 | 14ad7326 | pastith | } |
220 | 14ad7326 | pastith | throw new JSONException("Misplaced key."); |
221 | 14ad7326 | pastith | } |
222 | 14ad7326 | pastith | |
223 | 14ad7326 | pastith | |
224 | 14ad7326 | pastith | /**
|
225 | 14ad7326 | pastith | * Begin appending a new object. All keys and values until the balancing
|
226 | 14ad7326 | pastith | * <code>endObject</code> will be appended to this object. The
|
227 | 14ad7326 | pastith | * <code>endObject</code> method must be called to mark the object's end.
|
228 | 14ad7326 | pastith | * @return this
|
229 | 14ad7326 | pastith | * @throws JSONException If the nesting is too deep, or if the object is
|
230 | 14ad7326 | pastith | * started in the wrong place (for example as a key or after the end of the
|
231 | 14ad7326 | pastith | * outermost array or object).
|
232 | 14ad7326 | pastith | */
|
233 | 14ad7326 | pastith | public JSONWriter object() throws JSONException { |
234 | 14ad7326 | pastith | if (this.mode == 'i') { |
235 | 14ad7326 | pastith | this.mode = 'o'; |
236 | 14ad7326 | pastith | } |
237 | 14ad7326 | pastith | if (this.mode == 'o' || this.mode == 'a') { |
238 | 14ad7326 | pastith | this.append("{"); |
239 | 14ad7326 | pastith | this.push(new JSONObject()); |
240 | 14ad7326 | pastith | this.comma = false; |
241 | 14ad7326 | pastith | return this; |
242 | 14ad7326 | pastith | } |
243 | 14ad7326 | pastith | throw new JSONException("Misplaced object."); |
244 | 14ad7326 | pastith | |
245 | 14ad7326 | pastith | } |
246 | 14ad7326 | pastith | |
247 | 14ad7326 | pastith | |
248 | 14ad7326 | pastith | /**
|
249 | 14ad7326 | pastith | * Pop an array or object scope.
|
250 | 14ad7326 | pastith | * @param c The scope to close.
|
251 | 14ad7326 | pastith | * @throws JSONException If nesting is wrong.
|
252 | 14ad7326 | pastith | */
|
253 | 14ad7326 | pastith | private void pop(char c) throws JSONException { |
254 | 14ad7326 | pastith | if (this.top <= 0) { |
255 | 14ad7326 | pastith | throw new JSONException("Nesting error."); |
256 | 14ad7326 | pastith | } |
257 | 14ad7326 | pastith | char m = this.stack[this.top - 1] == null ? 'a' : 'k'; |
258 | 14ad7326 | pastith | if (m != c) {
|
259 | 14ad7326 | pastith | throw new JSONException("Nesting error."); |
260 | 14ad7326 | pastith | } |
261 | 14ad7326 | pastith | this.top -= 1; |
262 | 14ad7326 | pastith | this.mode = this.top == 0 ? 'd' : this.stack[this.top - 1] == null ? 'a' : 'k'; |
263 | 14ad7326 | pastith | } |
264 | 14ad7326 | pastith | |
265 | 14ad7326 | pastith | /**
|
266 | 14ad7326 | pastith | * Push an array or object scope.
|
267 | 14ad7326 | pastith | * @param c The scope to open.
|
268 | 14ad7326 | pastith | * @throws JSONException If nesting is too deep.
|
269 | 14ad7326 | pastith | */
|
270 | 14ad7326 | pastith | private void push(JSONObject jo) throws JSONException { |
271 | 14ad7326 | pastith | if (this.top >= maxdepth) { |
272 | 14ad7326 | pastith | throw new JSONException("Nesting too deep."); |
273 | 14ad7326 | pastith | } |
274 | 14ad7326 | pastith | this.stack[this.top] = jo; |
275 | 14ad7326 | pastith | this.mode = jo == null ? 'a' : 'k'; |
276 | 14ad7326 | pastith | this.top += 1; |
277 | 14ad7326 | pastith | } |
278 | 14ad7326 | pastith | |
279 | 14ad7326 | pastith | |
280 | 14ad7326 | pastith | /**
|
281 | 14ad7326 | pastith | * Append either the value <code>true</code> or the value
|
282 | 14ad7326 | pastith | * <code>false</code>.
|
283 | 14ad7326 | pastith | * @param b A boolean.
|
284 | 14ad7326 | pastith | * @return this
|
285 | 14ad7326 | pastith | * @throws JSONException
|
286 | 14ad7326 | pastith | */
|
287 | 14ad7326 | pastith | public JSONWriter value(boolean b) throws JSONException { |
288 | 14ad7326 | pastith | return this.append(b ? "true" : "false"); |
289 | 14ad7326 | pastith | } |
290 | 14ad7326 | pastith | |
291 | 14ad7326 | pastith | /**
|
292 | 14ad7326 | pastith | * Append a double value.
|
293 | 14ad7326 | pastith | * @param d A double.
|
294 | 14ad7326 | pastith | * @return this
|
295 | 14ad7326 | pastith | * @throws JSONException If the number is not finite.
|
296 | 14ad7326 | pastith | */
|
297 | 14ad7326 | pastith | public JSONWriter value(double d) throws JSONException { |
298 | 14ad7326 | pastith | return this.value(new Double(d)); |
299 | 14ad7326 | pastith | } |
300 | 14ad7326 | pastith | |
301 | 14ad7326 | pastith | /**
|
302 | 14ad7326 | pastith | * Append a long value.
|
303 | 14ad7326 | pastith | * @param l A long.
|
304 | 14ad7326 | pastith | * @return this
|
305 | 14ad7326 | pastith | * @throws JSONException
|
306 | 14ad7326 | pastith | */
|
307 | 14ad7326 | pastith | public JSONWriter value(long l) throws JSONException { |
308 | 14ad7326 | pastith | return this.append(Long.toString(l)); |
309 | 14ad7326 | pastith | } |
310 | 14ad7326 | pastith | |
311 | 14ad7326 | pastith | |
312 | 14ad7326 | pastith | /**
|
313 | 14ad7326 | pastith | * Append an object value.
|
314 | 14ad7326 | pastith | * @param o The object to append. It can be null, or a Boolean, Number,
|
315 | 14ad7326 | pastith | * String, JSONObject, or JSONArray, or an object with a toJSONString()
|
316 | 14ad7326 | pastith | * method.
|
317 | 14ad7326 | pastith | * @return this
|
318 | 14ad7326 | pastith | * @throws JSONException If the value is out of sequence.
|
319 | 14ad7326 | pastith | */
|
320 | 14ad7326 | pastith | public JSONWriter value(Object o) throws JSONException { |
321 | 14ad7326 | pastith | return this.append(JSONObject.valueToString(o)); |
322 | 14ad7326 | pastith | } |
323 | 14ad7326 | pastith | } |