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