001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.json;
014
015import org.apache.juneau.*;
016import org.apache.juneau.parser.*;
017
018/**
019 * Parses any valid JSON text into a POJO model.
020 *
021 * <h5 class='topic'>Media types</h5>
022 *
023 * Handles <code>Content-Type</code> types:  <code><b>application/json, text/json</b></code>
024 *
025 * <h5 class='topic'>Description</h5>
026 *
027 * This parser uses a state machine, which makes it very fast and efficient.  It parses JSON in about 70% of the
028 * time that it takes the built-in Java DOM parsers to parse equivalent XML.
029 *
030 * <p>
031 * This parser handles all valid JSON syntax.
032 * In addition, when strict mode is disable, the parser also handles the following:
033 * <ul class='spaced-list'>
034 *    <li>
035 *       Javascript comments (both {@code /*} and {@code //}) are ignored.
036 *    <li>
037 *       Both single and double quoted strings.
038 *    <li>
039 *       Automatically joins concatenated strings (e.g. <code><js>"aaa"</js> + <js>'bbb'</js></code>).
040 *    <li>
041 *       Unquoted attributes.
042 * </ul>
043 *
044 * <p>
045 * Also handles negative, decimal, hexadecimal, octal, and double numbers, including exponential notation.
046 *
047 * <p>
048 * This parser handles the following input, and automatically returns the corresponding Java class.
049 * <ul class='spaced-list'>
050 *    <li>
051 *       JSON objects (<js>"{...}"</js>) are converted to {@link ObjectMap ObjectMaps}.
052 *       <b>Note:</b>  If a <code><xa>_type</xa>=<xs>'xxx'</xs></code> attribute is specified on the object, then an
053 *       attempt is made to convert the object to an instance of the specified Java bean class.
054 *       See the <code>beanTypeName</code> setting on the {@link PropertyStore} for more information about parsing
055 *       beans from JSON.
056 *    <li>
057 *       JSON arrays (<js>"[...]"</js>) are converted to {@link ObjectList ObjectLists}.
058 *    <li>
059 *       JSON string literals (<js>"'xyz'"</js>) are converted to {@link String Strings}.
060 *    <li>
061 *       JSON numbers (<js>"123"</js>, including octal/hexadecimal/exponential notation) are converted to
062 *       {@link Integer Integers}, {@link Long Longs}, {@link Float Floats}, or {@link Double Doubles} depending on
063 *       whether the number is decimal, and the size of the number.
064 *    <li>
065 *       JSON booleans (<js>"false"</js>) are converted to {@link Boolean Booleans}.
066 *    <li>
067 *       JSON nulls (<js>"null"</js>) are converted to <jk>null</jk>.
068 *    <li>
069 *       Input consisting of only whitespace or JSON comments are converted to <jk>null</jk>.
070 * </ul>
071 *
072 * <p>
073 * Input can be any of the following:
074 * <ul class='spaced-list'>
075 *    <li>
076 *       <js>"{...}"</js> - Converted to a {@link ObjectMap} or an instance of a Java bean if a <xa>_type</xa>
077 *       attribute is present.
078 *    <li>
079 *       <js>"[...]"</js> - Converted to a {@link ObjectList}.
080 *    <li>
081 *       <js>"123..."</js> - Converted to a {@link Number} (either {@link Integer}, {@link Long}, {@link Float},
082 *       or {@link Double}).
083 *    <li>
084 *       <js>"true"</js>/<js>"false"</js> - Converted to a {@link Boolean}.
085 *    <li>
086 *       <js>"null"</js> - Returns <jk>null</jk>.
087 *    <li>
088 *       <js>"'xxx'"</js> - Converted to a {@link String}.
089 *    <li>
090 *       <js>"\"xxx\""</js> - Converted to a {@link String}.
091 *    <li>
092 *       <js>"'xxx' + \"yyy\""</js> - Converted to a concatenated {@link String}.
093 * </ul>
094 *
095 * <p>
096 * TIP:  If you know you're parsing a JSON object or array, it can be easier to parse it using the
097 * {@link ObjectMap#ObjectMap(CharSequence) ObjectMap(CharSequence)} or {@link ObjectList#ObjectList(CharSequence)
098 * ObjectList(CharSequence)} constructors instead of using this class.
099 * The end result should be the same.
100 */
101public class JsonParser extends ReaderParser {
102
103   //-------------------------------------------------------------------------------------------------------------------
104   // Configurable properties
105   //-------------------------------------------------------------------------------------------------------------------
106
107   private static final String PREFIX = "JsonParser.";
108
109   /**
110    * Configuration property:  Validate end.
111    *
112    * <h5 class='section'>Property:</h5>
113    * <ul>
114    *    <li><b>Name:</b>  <js>"JsonParser.validateEnd.b"</js>
115    *    <li><b>Data type:</b>  <code>Boolean</code>
116    *    <li><b>Default:</b>  <jk>false</jk>
117    *    <li><b>Session property:</b>  <jk>false</jk>
118    *    <li><b>Methods:</b>
119    *       <ul>
120    *          <li class='jm'>{@link JsonParserBuilder#validateEnd(boolean)}
121    *          <li class='jm'>{@link JsonParserBuilder#validateEnd()}
122    *       </ul>
123    * </ul>
124    *
125    * <h5 class='section'>Description:</h5>
126    * <p>
127    * If <jk>true</jk>, after parsing a POJO from the input, verifies that the remaining input in
128    * the stream consists of only comments or whitespace.
129    *
130    * <h5 class='section'>Example:</h5>
131    * <p class='bcode w800'>
132    *    <jc>// Create a parser that validates that there's no garbage at the end of the input.</jc>
133    *    ReaderParser p = JsonParser.
134    *       .<jsm>create</jsm>()
135    *       .validateEnd()
136    *       .build();
137    *
138    *    <jc>// Same, but use property.</jc>
139    *    ReaderParser p = JsonParser.
140    *       .<jsm>create</jsm>()
141    *       .set(<jsf>JSON_validateEnd</jsf>, <jk>true</jk>)
142    *       .build();
143    *
144    *    <jc>// Should fail because input has multiple POJOs.</jc>
145    *    String in = <js>"{foo:'bar'}{baz:'qux'}"</js>;
146    *    MyBean myBean = p.parse(in, MyBean.<jk>class</jk>);
147    * </p>
148    */
149   public static final String JSON_validateEnd = PREFIX + "validateEnd.b";
150
151   //-------------------------------------------------------------------------------------------------------------------
152   // Predefined instances
153   //-------------------------------------------------------------------------------------------------------------------
154
155   /** Default parser, all default settings.*/
156   public static final JsonParser DEFAULT = new JsonParser.Simple(PropertyStore.DEFAULT);
157
158   /** Default parser, all default settings.*/
159   public static final JsonParser DEFAULT_STRICT = new JsonParser.Strict(PropertyStore.DEFAULT);
160
161
162   //-------------------------------------------------------------------------------------------------------------------
163   // Predefined subclasses
164   //-------------------------------------------------------------------------------------------------------------------
165
166   /** Default parser, strict mode. */
167   public static class Strict extends JsonParser {
168
169      /**
170       * Constructor.
171       *
172       * @param ps The property store containing all the settings for this object.
173       */
174      public Strict(PropertyStore ps) {
175         super(ps.builder().set(PARSER_strict, true).set(JSON_validateEnd, true).build());
176      }
177   }
178
179   /** Default parser, simple mode. */
180   public static class Simple extends JsonParser {
181
182      /**
183       * Constructor.
184       *
185       * @param ps The property store containing all the settings for this object.
186       */
187      public Simple(PropertyStore ps) {
188         super(ps, "application/json+simple", "text/json+simple");
189      }
190   }
191
192   //-------------------------------------------------------------------------------------------------------------------
193   // Instance
194   //-------------------------------------------------------------------------------------------------------------------
195
196   private final boolean validateEnd;
197
198   /**
199    * Constructor.
200    *
201    * @param ps The property store containing all the settings for this object.
202    */
203   public JsonParser(PropertyStore ps) {
204      this(ps, "application/json", "text/json");
205   }
206
207   /**
208    * Constructor.
209    *
210    * @param ps The property store containing all the settings for this object.
211    * @param consumes The list of media types that this parser consumes (e.g. <js>"application/json"</js>).
212    */
213   public JsonParser(PropertyStore ps, String...consumes) {
214      super(ps, consumes);
215      validateEnd = getBooleanProperty(JSON_validateEnd, false);
216   }
217
218   @Override /* Context */
219   public JsonParserBuilder builder() {
220      return new JsonParserBuilder(getPropertyStore());
221   }
222
223   /**
224    * Instantiates a new clean-slate {@link JsonParserBuilder} object.
225    *
226    * <p>
227    * This is equivalent to simply calling <code><jk>new</jk> JsonParserBuilder()</code>.
228    *
229    * <p>
230    * Note that this method creates a builder initialized to all default settings, whereas {@link #builder()} copies
231    * the settings of the object called on.
232    *
233    * @return A new {@link JsonParserBuilder} object.
234    */
235   public static JsonParserBuilder create() {
236      return new JsonParserBuilder();
237   }
238
239   @Override /* Parser */
240   public ReaderParserSession createSession(ParserSessionArgs args) {
241      return new JsonParserSession(this, args);
242   }
243
244   //-----------------------------------------------------------------------------------------------------------------
245   // Properties
246   //-----------------------------------------------------------------------------------------------------------------
247
248   /**
249    * Configuration property:  Validate end.
250    *
251    * @see #JSON_validateEnd
252    * @return
253    *    <jk>true</jk> if after parsing a POJO from the input, verifies that the remaining input in
254    *    the stream consists of only comments or whitespace.
255    */
256   protected final boolean isValidateEnd() {
257      return validateEnd;
258   }
259
260   @Override /* Context */
261   public ObjectMap asMap() {
262      return super.asMap()
263         .append("JsonParser", new ObjectMap());
264   }
265}