1 /*
2 * #%L
3 * Json.java - mongodb-async-driver - Allanbank Consulting, Inc.
4 * %%
5 * Copyright (C) 2011 - 2014 Allanbank Consulting, Inc.
6 * %%
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * #L%
19 */
20
21 package com.allanbank.mongodb.bson.json;
22
23 import java.io.Reader;
24 import java.io.StringReader;
25 import java.io.StringWriter;
26 import java.io.Writer;
27
28 import com.allanbank.mongodb.bson.Document;
29 import com.allanbank.mongodb.bson.DocumentAssignable;
30 import com.allanbank.mongodb.bson.DocumentReference;
31 import com.allanbank.mongodb.bson.Element;
32 import com.allanbank.mongodb.bson.element.BinaryElement;
33 import com.allanbank.mongodb.bson.element.BooleanElement;
34 import com.allanbank.mongodb.bson.element.DoubleElement;
35 import com.allanbank.mongodb.bson.element.IntegerElement;
36 import com.allanbank.mongodb.bson.element.JsonSerializationVisitor;
37 import com.allanbank.mongodb.bson.element.LongElement;
38 import com.allanbank.mongodb.bson.element.MaxKeyElement;
39 import com.allanbank.mongodb.bson.element.MinKeyElement;
40 import com.allanbank.mongodb.bson.element.MongoTimestampElement;
41 import com.allanbank.mongodb.bson.element.NullElement;
42 import com.allanbank.mongodb.bson.element.ObjectIdElement;
43 import com.allanbank.mongodb.bson.element.RegularExpressionElement;
44 import com.allanbank.mongodb.bson.element.StringElement;
45 import com.allanbank.mongodb.bson.element.SymbolElement;
46 import com.allanbank.mongodb.bson.element.TimestampElement;
47 import com.allanbank.mongodb.error.JsonException;
48 import com.allanbank.mongodb.error.JsonParseException;
49
50 /**
51 * Json provides a simplified interface for parsing JSON documents into BSON
52 * {@link Document}s and also serializing BSON {@link Document}s into JSON text.
53 * <p>
54 * Basic JSON types are parsed as follows:
55 * <dl>
56 * <dt>{@code true} or {@code false} token</dt>
57 * <dd>Creates an {@link BooleanElement}. <br/>
58 * <code>{ a : true }</code> or <code>{ a : false }</code></dd>
59 * <dt>{@code null} token</dt>
60 * <dd>Creates an {@link NullElement}. <br/>
61 * <code>{ a : null }</code></dd>
62 * <dt>Other Non-Quoted Strings</dt>
63 * <dd>Creates a {@link SymbolElement}:<br/>
64 * <code>{ a : b }</code></dd>
65 * <dt>Quoted Strings (either single or double quotes)</dt>
66 * <dd>Creates a {@link StringElement}:<br/>
67 * <code>{ a : 'b' }</code> or <code>{ a : "b" }</code></dd>
68 * <dt>Integers (Numbers without a {@code . } or exponent)</dt>
69 * <dd>Creates an {@link IntegerElement} if within the range [
70 * {@link Integer#MIN_VALUE}, {@link Integer#MAX_VALUE}], otherwise a
71 * {@link LongElement}. Value is parsed by {@link Long#parseLong(String)}.<br/>
72 * <code>{ a : 1234 }</code> or <code>{ a : 123456789012 }</code></dd>
73 * <dt>Doubles (Numbers with a {@code . } or exponent)</dt>
74 * <dd>Creates an {@link DoubleElement}. Value is parsed by
75 * {@link Double#parseDouble(String)}.<br/>
76 * <code>{ a : 1.2 }</code> or <code>{ a : 1e12 }</code></dd>
77 * </p>
78 * <p>
79 * In addition to the basic JSON types the parser also supports the following
80 * standard MongoDB/BSON extensions:
81 * </p>
82 * <dl>
83 * <dt>BinData</dt>
84 * <dd>Creates a {@link BinaryElement}. The first field is the sub-type,
85 * normally zero. The second field is the base64 encoded binary value: <br/>
86 * <code>{ a : BinData(0, "VVU=") }</code> or
87 * <code>{ a : { $binary:"VVU=", $type:0 } }</code></dd>
88 * <code>{ a : { $binary:"VVU=", $type: '0x00' } }</code></dd>
89 * <code>{ a : { $binary:"VVU=", $type: '00' } }</code></dd>
90 * <dt>HexData</dt>
91 * <dd>Creates a {@link BinaryElement}. The first field is the sub-type,
92 * normally zero. The second field is the hex encoded binary value: <br/>
93 * <code>{ a : HexData(0, "cafe") }</code></dd>
94 * <dt>ISODate</dt>
95 * <dd>Creates a {@link TimestampElement}: <br/>
96 * <code>{ a : ISODate("2012-07-14T01:00:00.000") }</code> or
97 * <code>{ a : { $date : "2012-07-14T01:00:00.000" } }</code> or
98 * <code>{ a : { $date : 1234567890 } }</code></dd>
99 * <dt>MaxKey</dt>
100 * <dd>Creates a {@link MaxKeyElement}: <br/>
101 * <code>{ a : MaxKey }</code> or <code>{ a : MaxKey() }</code></dd>
102 * <dt>MinKey</dt>
103 * <dd>Creates a {@link MinKeyElement}: <br/>
104 * <code>{ a : MinKey }</code> or <code>{ a : MinKey() }</code></dd>
105 * <dt>NumberLong</dt>
106 * <dd>Creates a {@link LongElement}: <br/>
107 * <code>{ a : NumberLong("123456789") }</code></dd>
108 * <dt>ObjectId</dt>
109 * <dd>Creates an {@link ObjectIdElement}. The string is the hex encoding of the
110 * 128 bit value: <br/>
111 * <code>{ a : ObjectId("4e9d87aa5825b60b637815a6") }</code> or
112 * <code>{ a : { $oid : "4e9d87aa5825b60b637815a6" } }</code></dd>
113 * <dt>$regex</dt>
114 * <dd>Creates an {@link RegularExpressionElement}: <br/>
115 * <code>{ a : { $regex : 'cat' , $options : 'i' } }</code></dd>
116 * <dt>Timestamp</dt>
117 * <dd>Creates a {@link MongoTimestampElement}. The first value is the seconds
118 * since the UNIX epoch. The second value is an ordinal: <br/>
119 * <code>{ a : Timestamp(0,0) }</code> or
120 * <code>{ a : { $timestamp : { t : 0, i : 0 } } }</code></dd>
121 * </dl>
122 * <p>
123 * The following non-standard extensions are also provided. These extensions may
124 * be deprecated in future releases if standard extensions are created:
125 * </p>
126 * <dl>
127 * <dt>DBPointer</dt>
128 * <dd>Creates a {@link com.allanbank.mongodb.bson.element.DBPointerElement
129 * DBPointerElement}:<br/>
130 * <code>{ a : DBPointer("db", 'collection', ObjectId("4e9d87aa5825b60b637815a6") ) }</code>
131 * <br/>
132 * <b>Note</b>: DBPointers are deprecated in favor of the
133 * {@link DocumentReference DBRef} convention</dd>
134 * </dl>
135 * <p>
136 * <b>Note</b>: Currently serialization/parsing round trip is not supported for
137 * the following {@link Element} types:
138 * <ul>
139 * <li>{@link com.allanbank.mongodb.bson.element.JavaScriptElement
140 * JavaScriptElement}</li>
141 * <li>{@link com.allanbank.mongodb.bson.element.JavaScriptWithScopeElement
142 * JavaScriptWithScopeElement}</li>
143 * </ul>
144 * </p>
145 *
146 * @see <a
147 * href="http://docs.mongodb.org/manual/reference/mongodb-extended-json/">MongoDB
148 * Extended JSON</a>
149 * @api.yes This class is part of the driver's API. Public and protected members
150 * will be deprecated for at least 1 non-bugfix release (version
151 * numbers are <major>.<minor>.<bugfix>) before being
152 * removed or modified.
153 * @copyright 2012-2013, Allanbank Consulting, Inc., All Rights Reserved
154 */
155 public class Json {
156
157 /**
158 * Parses the document from the reader into a BSON {@link Document}.
159 * <p>
160 * See the class documentation for important limitations on round trip
161 * serialization and parsing.
162 * </p>
163 *
164 * @param input
165 * The source of the document to read.
166 * @return The {@link Document} representation of the JSON document.
167 * @throws JsonParseException
168 * On a failure to parse the JSON document.
169 */
170 public static Document parse(final Reader input) throws JsonParseException {
171 final JsonParser parser = new JsonParser();
172
173 try {
174 final Object result = parser.parse(input);
175 if (result instanceof Document) {
176 return (Document) result;
177 }
178
179 throw new JsonParseException(
180 "Unknown type returned from the parsed document: " + result);
181 }
182 catch (final ParseException pe) {
183 if (pe.currentToken != null) {
184 throw new JsonParseException(pe.getMessage(), pe,
185 pe.currentToken.beginLine, pe.currentToken.beginColumn);
186 }
187 throw new JsonParseException(pe);
188 }
189 catch (final RuntimeException re) {
190 throw new JsonParseException(re);
191 }
192 }
193
194 /**
195 * Parses the document from the reader into a BSON {@link Document}.
196 * <p>
197 * This method is equivalent to: <blockquote>
198 *
199 * <pre>
200 * <code>
201 * parse(new StringReader(input));
202 * </code>
203 * </pre>
204 *
205 * </blockquote>
206 * </p>
207 * <p>
208 * See the class documentation for important limitations on round trip
209 * serialization and parsing.
210 * </p>
211 *
212 * @param input
213 * The source of the document to read.
214 * @return The {@link Document} representation of the JSON document.
215 * @throws JsonParseException
216 * On a failure to parse the JSON document.
217 */
218 public static Document parse(final String input) throws JsonParseException {
219 return parse(new StringReader(input));
220 }
221
222 /**
223 * Serializes the {@link Document} to an equivalent JSON document.
224 * <p>
225 * See the class documentation for important limitations on round trip
226 * serialization and parsing.
227 * </p>
228 *
229 * @param document
230 * The document to conver to a JSON document.
231 * @return The JSON document text.
232 * @throws JsonException
233 * On a failure to write the JSON document.
234 */
235 public static String serialize(final DocumentAssignable document)
236 throws JsonException {
237 final StringWriter writer = new StringWriter();
238
239 serialize(document, writer);
240
241 return writer.toString();
242 }
243
244 /**
245 * Serializes the {@link Document} to an equivalent JSON document.
246 * <p>
247 * See the class documentation for important limitations on round trip
248 * serialization and parsing.
249 * </p>
250 *
251 * @param document
252 * The document to conver to a JSON document.
253 * @param sink
254 * The sink for the JSON document text.
255 * @throws JsonException
256 * On a failure to write the JSON document.
257 */
258 public static void serialize(final DocumentAssignable document,
259 final Writer sink) throws JsonException {
260 final JsonSerializationVisitor visitor = new JsonSerializationVisitor(
261 sink, true);
262 document.asDocument().accept(visitor);
263 }
264
265 /**
266 * Creates a new Json onbject - hidden.
267 */
268 private Json() {
269 super();
270 }
271
272 }