View Javadoc
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 &lt;major&gt;.&lt;minor&gt;.&lt;bugfix&gt;) 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 }