org.dominokit.jacksonapt.stream.impl.DefaultJsonReader Maven / Gradle / Ivy
/*
* Copyright © 2019 Dominokit
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// @formatter:off
/*
* Copyright (C) 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dominokit.jacksonapt.stream.impl;
import java.math.BigInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.dominokit.jacksonapt.JacksonContextProvider;
import org.dominokit.jacksonapt.exception.JsonDeserializationException;
import org.dominokit.jacksonapt.stream.JsonToken;
import org.dominokit.jacksonapt.stream.JsonWriter;
import org.dominokit.jacksonapt.stream.Stack;
/**
* Reads a JSON (RFC 4627) encoded value as a
* stream of tokens. This stream includes both literal values (strings, numbers, booleans, and
* nulls) as well as the begin and end delimiters of objects and arrays. The tokens are traversed in
* depth-first order, the same order that they appear in the JSON document. Within JSON objects,
* name/value pairs are represented by a single token.
*
* Parsing JSON
*
* To create a recursive descent parser for your own JSON streams, first create an entry point
* method that creates a {@code JsonReader}.
*
* Next, create handler methods for each structure in your JSON text. You'll need a method for
* each object type and for each array type.
*
*
* - Within array handling methods, first call {@link #beginArray} to consume
* the array's opening bracket. Then create a while loop that accumulates values, terminating
* when {@link #hasNext} is false. Finally, read the array's closing bracket by calling {@link
* #endArray}.
*
- Within object handling methods, first call {@link #beginObject} to consume
* the object's opening brace. Then create a while loop that assigns values to local variables
* based on their name. This loop should terminate when {@link #hasNext} is false. Finally,
* read the object's closing brace by calling {@link #endObject}.
*
*
* When a nested object or array is encountered, delegate to the corresponding handler method.
*
*
When an unknown name is encountered, strict parsers should fail with an exception. Lenient
* parsers should call {@link #skipValue()} to recursively skip the value's nested tokens, which may
* otherwise conflict.
*
*
If a value may be null, you should first check using {@link #peek()}. Null literals can be
* consumed using either {@link #nextNull()} or {@link #skipValue()}.
*
*
Example
*
* Suppose we'd like to parse a stream of messages such as the following:
*
* {@code
* [
* {
* "id": 912345678901,
* "text": "How do I read a JSON stream in Java?",
* "geo": null,
* "user": {
* "name": "json_newb",
* "followers_count": 41
* }
* },
* {
* "id": 912345678902,
* "text": "@json_newb just use JsonReader!",
* "geo": [50.454722, -104.606667],
* "user": {
* "name": "jesse",
* "followers_count": 2
* }
* }
* ]
* }
*
* This code implements the parser for the above structure:
*
* {@code
*
* public List readJsonStream(InputStream in) {
* JsonReader reader = new JsonReader(new InputStreamReader(in, "UTF-8"));
* try {
* return readMessagesArray(reader);
* } finally {
* reader.close();
* }
* }
*
* public List readMessagesArray(JsonReader reader) {
* List messages = new ArrayList();
*
* reader.beginArray();
* while (reader.hasNext()) {
* messages.add(readMessage(reader));
* }
* reader.endArray();
* return messages;
* }
*
* public Message readMessage(JsonReader reader) {
* long id = -1;
* String text = null;
* User user = null;
* List geo = null;
*
* reader.beginObject();
* while (reader.hasNext()) {
* String name = reader.nextName();
* if (name.equals("id")) {
* id = reader.nextLong();
* } else if (name.equals("text")) {
* text = reader.nextString();
* } else if (name.equals("geo") && reader.peek() != JsonToken.NULL) {
* geo = readDoublesArray(reader);
* } else if (name.equals("user")) {
* user = readUser(reader);
* } else {
* reader.skipValue();
* }
* }
* reader.endObject();
* return new Message(id, text, user, geo);
* }
*
* public List readDoublesArray(JsonReader reader) {
* List doubles = new ArrayList();
*
* reader.beginArray();
* while (reader.hasNext()) {
* doubles.add(reader.nextDouble());
* }
* reader.endArray();
* return doubles;
* }
*
* public User readUser(JsonReader reader) {
* String username = null;
* int followersCount = -1;
*
* reader.beginObject();
* while (reader.hasNext()) {
* String name = reader.nextName();
* if (name.equals("name")) {
* username = reader.nextString();
* } else if (name.equals("followers_count")) {
* followersCount = reader.nextInt();
* } else {
* reader.skipValue();
* }
* }
* reader.endObject();
* return new User(username, followersCount);
* }
* }
*
* Number Handling
*
* This reader permits numeric values to be read as strings and string values to be read as numbers.
* For example, both elements of the JSON array {@code [1, "1"]} may be read using either {@link
* #nextInt} or {@link #nextString}. This behavior is intended to prevent lossy numeric conversions:
* double is JavaScript's only numeric type and very large values like {@code 9007199254740993}
* cannot be represented exactly on that platform. To minimize precision loss, extremely large
* values should be written and read as strings in JSON.
*
* Non-Execute Prefix
*
* Web servers that serve private data using JSON may be vulnerable to Cross-site request
* forgery attacks. In such an attack, a malicious site gains access to a private JSON file by
* executing it with an HTML {@code