001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.marshall.json; 018 019import static org.apache.juneau.commons.utils.Shorts.*; 020import static org.apache.juneau.commons.utils.SystemUtils.*; 021 022import java.io.*; 023import java.util.*; 024import java.util.concurrent.*; 025 026import org.apache.juneau.commons.bean.*; 027import org.apache.juneau.commons.collections.*; 028import org.apache.juneau.marshall.*; 029import org.apache.juneau.marshall.collections.*; 030import org.apache.juneau.marshall.json5.*; 031import org.apache.juneau.marshall.parser.*; 032import org.apache.juneau.marshall.stream.*; 033 034/** 035 * Parses any valid RFC 8259 JSON text into a POJO model. 036 * 037 * <p> 038 * This parser strictly enforces RFC 8259 JSON syntax. Double-quoted strings only; single-quoted strings, 039 * comments, trailing commas, and unquoted keys are rejected. For JSON5 syntax (single quotes, comments, 040 * etc.), use {@link Json5Parser}. 041 * 042 * <h5 class='topic'>Media types</h5> 043 * <p> 044 * Handles <c>Content-Type</c> types: <bc>application/json, text/json, application/jcs+json</bc> 045 * 046 * <h5 class='topic'>Description</h5> 047 * <p> 048 * This parser uses a state machine, which makes it very fast and efficient. It parses JSON in about 70% of the 049 * time that it takes the built-in Java DOM parsers to parse equivalent XML. 050 * 051 * <p> 052 * This parser handles all valid RFC 8259 JSON syntax. 053 * 054 * <p> 055 * Also handles negative, decimal, hexadecimal, octal, and double numbers, including exponential notation. 056 * 057 * <p> 058 * This parser handles the following input, and automatically returns the corresponding Java class. 059 * <ul class='spaced-list'> 060 * <li> 061 * JSON objects (<js>"{...}"</js>) are converted to {@link JsonMap JsonMaps}. 062 * <b>Note:</b> If a <code><xa>_type</xa>=<xs>'xxx'</xs></code> attribute is specified on the object, then an 063 * attempt is made to convert the object to an instance of the specified Java bean class. 064 * See the {@link org.apache.juneau.marshall.MarshallingContext.Builder#typePropertyName(String) MarshallingContext.Builder#typePropertyName(String)} setting for more information about parsing 065 * beans from JSON. 066 * <li> 067 * JSON arrays (<js>"[...]"</js>) are converted to {@link JsonList JsonLists}. 068 * <li> 069 * JSON string literals (<js>"\"xyz\""</js>) are converted to {@link String Strings}. 070 * <li> 071 * JSON numbers (<js>"123"</js>, including octal/hexadecimal/exponential notation) are converted to 072 * {@link Integer Integers}, {@link Long Longs}, {@link Float Floats}, or {@link Double Doubles} depending on 073 * whether the number is decimal, and the size of the number. 074 * <li> 075 * JSON booleans (<js>"false"</js>) are converted to {@link Boolean Booleans}. 076 * <li> 077 * JSON nulls (<js>"null"</js>) are converted to <jk>null</jk>. 078 * </ul> 079 * 080 * <p> 081 * Input can be any of the following: 082 * <ul class='spaced-list'> 083 * <li> 084 * <js>"{...}"</js> - Converted to an {@link JsonMap} or an instance of a Java bean if a <xa>_type</xa> 085 * attribute is present. 086 * <li> 087 * <js>"[...]"</js> - Converted to an {@link JsonList}. 088 * <li> 089 * <js>"123..."</js> - Converted to a {@link Number} (either {@link Integer}, {@link Long}, {@link Float}, 090 * or {@link Double}). 091 * <li> 092 * <js>"true"</js>/<js>"false"</js> - Converted to a {@link Boolean}. 093 * <li> 094 * <js>"null"</js> - Returns <jk>null</jk>. 095 * <li> 096 * <js>"\"xxx\""</js> - Converted to a {@link String} (double quotes only). 097 * </ul> 098 * 099 * <p> 100 * TIP: If you know you're parsing a JSON object or array, it can be easier to parse it using the 101 * {@link JsonMap#JsonMap(CharSequence) JsonMap(CharSequence)} or {@link JsonList#JsonList(CharSequence) 102 * JsonList(CharSequence)} constructors instead of using this class. 103 * The end result should be the same. 104 * 105 * <h5 class='figure'>Example input (Map of name/age):</h5> 106 * <p class='bjson'> 107 * {<js>"name"</js>:<js>"Alice"</js>,<js>"age"</js>:30} 108 * </p> 109 * 110 * <h5 class='figure'>Complex (nested object + array):</h5> 111 * <p class='bjson'> 112 * {<js>"name"</js>:<js>"Alice"</js>,<js>"age"</js>:30,<js>"address"</js>:{<js>"street"</js>:<js>"123 Main St"</js>,<js>"city"</js>:<js>"Boston"</js>,<js>"state"</js>:<js>"MA"</js>},<js>"tags"</js>:[<js>"a"</js>,<js>"b"</js>,<js>"c"</js>]} 113 * </p> 114 * 115 * <h5 class='section'>Notes:</h5><ul> 116 * <li class='note'>This class is thread safe and reusable. 117 * </ul> 118 * 119 * <h5 class='section'>See Also:</h5><ul> 120 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JsonSupport">JSON</a> 121 * <li class='link'>{@link Json5Parser} - For JSON5 syntax (single quotes, comments, etc.) 122 * </ul> 123 */ 124@SuppressWarnings({ 125 "java:S110", // Inheritance depth acceptable 126 "java:S1192", // Duplicated literals (argument/property names) read more clearly inline than as constants 127 "java:S9149", // Per-format static factories intentionally shadow the parent's. 128 "resource" // readTokens(...) returns a Closeable owned by the caller; Eclipse JDT @Owning warning is by design. 129}) 130public class JsonParser extends ReaderParser implements JsonMetaProvider, TokenReadable, ArrayRecordReadable { 131 132 /** 133 * Builder class. 134 * 135 * <p> 136 * Unlike the abstract context builders, this builder is concrete <i>and</i> generic: it terminates the CRTP 137 * recursion via the leaf-free {@code new Builder<>()} pattern (see {@link JsonParser#create()} / 138 * {@link JsonParser#copy()}) rather than via a separate concrete {@code DefaultBuilder} leaf. 139 */ 140 @SuppressWarnings({ 141 "java:S119" // 'SELF' (CRTP self-type) is intentional and clearer than a single-letter name. 142 }) 143 public static class Builder<SELF extends Builder<SELF>> extends ReaderParser.Builder<SELF> { 144 145 private static final Cache<HashKey,JsonParser> CACHE = Cache.of(HashKey.class, JsonParser.class).build(); 146 147 private boolean validateEnd; 148 149 /** 150 * Constructor, default settings. 151 */ 152 protected Builder() { 153 consumes("application/json,text/json,application/jcs+json"); 154 validateEnd = env("JsonParser.validateEnd", false); 155 } 156 157 /** 158 * Copy constructor. 159 * 160 * @param copyFrom The builder to copy from. 161 * <br>Cannot be <jk>null</jk>. 162 */ 163 protected Builder(Builder<?> copyFrom) { 164 super(reqnn("copyFrom", copyFrom)); 165 validateEnd = copyFrom.validateEnd; 166 } 167 168 /** 169 * Copy constructor. 170 * 171 * @param copyFrom The bean to copy from. 172 * <br>Cannot be <jk>null</jk>. 173 */ 174 protected Builder(JsonParser copyFrom) { 175 super(reqnn("copyFrom", copyFrom)); 176 validateEnd = copyFrom.validateEnd; 177 } 178 179 @Override /* Overridden from Context.Builder<?> */ 180 public JsonParser build() { 181 return cache(CACHE).build(JsonParser.class); 182 } 183 184 @Override /* Overridden from Context.Builder<?> */ 185 @SuppressWarnings({ 186 "unchecked" // CRTP self-type cast on the leaf-free Builder copy is safe by construction. 187 }) 188 public SELF copy() { 189 return (SELF) new Builder<>(this); 190 } 191 192 @Override /* Overridden from Context.Builder<?> */ 193 public HashKey hashKey() { 194 return HashKey.of(super.hashKey(), validateEnd); 195 } 196 197 198 /** 199 * Validate end. 200 * 201 * <p> 202 * When enabled, after parsing a POJO from the input, verifies that the remaining input in 203 * the stream consists of only comments or whitespace. 204 * 205 * <h5 class='section'>Example:</h5> 206 * <p class='bjava'> 207 * <jc>// Create a parser that validates that there's no garbage at the end of the input.</jc> 208 * ReaderParser <jv>parser</jv> = JsonParser. 209 * .<jsm>create</jsm>() 210 * .validateEnd() 211 * .build(); 212 * 213 * <jc>// Should fail because input has multiple POJOs.</jc> 214 * String <jv>json</jv> = <js>"{foo:'bar'}{baz:'qux'}"</js>; 215 * MyBean <jv>myBean</jv> =<jv>parser</jv>.read(<jv>json</jv>, MyBean.<jk>class</jk>); 216 * </p> 217 * 218 * <p> 219 * <b>Token streams:</b> this setting is not honored by {@link #readTokens(Object) readTokens} 220 * — the cursor leaves end-of-input checking to the caller. 221 * 222 * @return This object. 223 */ 224 public SELF validateEnd() { 225 return validateEnd(true); 226 } 227 228 /** 229 * Same as {@link #validateEnd()} but allows you to explicitly specify the value. 230 * 231 * @param value The value for this setting. 232 * @return This object. 233 */ 234 public SELF validateEnd(boolean value) { 235 validateEnd = value; 236 return self(); 237 } 238 } 239 240 /** Default parser, all default settings.*/ 241 public static final JsonParser DEFAULT = new JsonParser(create()); 242 243 /** 244 * Creates a new builder for this object. 245 * 246 * @return A new builder. 247 */ 248 @SuppressWarnings({ 249 "java:S1452" // Self-typed builder: Builder<?> is the only non-raw, leaf-free return; chaining + build() unaffected. 250 }) 251 public static Builder<?> create() { 252 return new Builder<>(); 253 } 254 255 protected final boolean validateEnd; 256 257 private final Map<BeanPropertyMeta,JsonBeanPropertyMeta> jsonBeanPropertyMetas = new ConcurrentHashMap<>(); 258 private final Map<ClassMeta<?>,JsonClassMeta> jsonClassMetas = new ConcurrentHashMap<>(); 259 260 261 /** 262 * Constructor. 263 * 264 * @param builder The builder for this object. 265 */ 266 public JsonParser(Builder<?> builder) { 267 super(builder); 268 validateEnd = builder.validateEnd; 269 } 270 271 @Override /* Overridden from Context */ 272 public Builder<?> copy() { 273 return new Builder<>(this); 274 } 275 276 @Override /* Overridden from Context */ 277 public JsonParserSession.Builder<?> createSession() { 278 return JsonParserSession.create(this); 279 } 280 281 @Override /* Overridden from JsonMetaProvider */ 282 public JsonBeanPropertyMeta getJsonBeanPropertyMeta(BeanPropertyMeta bpm) { 283 if (bpm == null) 284 return JsonBeanPropertyMeta.DEFAULT; 285 return jsonBeanPropertyMetas.computeIfAbsent(bpm, k -> new JsonBeanPropertyMeta(k.getDelegateFor(), this)); 286 } 287 288 @Override /* Overridden from JsonMetaProvider */ 289 public JsonClassMeta getJsonClassMeta(ClassMeta<?> cm) { 290 return jsonClassMetas.computeIfAbsent(cm, k -> new JsonClassMeta(k, this)); 291 } 292 293 @Override /* Overridden from Context */ 294 public JsonParserSession getSession() { return createSession().build(); } 295 296 /** 297 * Convenience delegator that opens a {@link JsonTokenReader} over the input using 298 * <b>default session arguments</b> (mirrors {@link #read(Object, Class)}). 299 * 300 * <p> 301 * The real implementation lives on {@link JsonParserSession#readTokens(Object)}. Callers 302 * that need request-derived configuration (locale, timezone, schema, swaps) should call 303 * {@link #createSession()} and invoke {@link JsonParserSession#readTokens(Object)} on the 304 * built session instead. 305 * 306 * @param input The input. Accepts {@link Reader}, {@link CharSequence}, {@link InputStream}, 307 * <code><jk>byte</jk>[]</code>, or {@link File}. 308 * @return A new {@link JsonTokenReader}. 309 * @throws IOException If a problem occurred opening the underlying input. 310 */ 311 @Override /* TokenReadable */ 312 public TokenReader readTokens(Object input) throws IOException { 313 return getSession().readTokens(input); 314 } 315 316 /** 317 * Validate end. 318 * 319 * @see Builder#validateEnd() 320 * @return 321 * <jk>true</jk> if after parsing a POJO from the input, verifies that the remaining input in 322 * the stream consists of only comments or whitespace. 323 */ 324 protected final boolean isValidateEnd() { return validateEnd; } 325 326 /** 327 * Convenience delegator for the streaming array-element {@link RecordReader} (uses default 328 * session args; see {@link #readTokens(Object)}). Real impl on 329 * {@link JsonParserSession#readArrayRecords(Object)}. 330 * 331 * @param input The input. 332 * @return A new element-streamed {@link RecordReader}. 333 * @throws IOException If a problem occurred opening the underlying input. 334 */ 335 @Override /* ArrayRecordReadable */ 336 public RecordReader readArrayRecords(Object input) throws IOException { 337 return getSession().readArrayRecords(input); 338 } 339}