All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.dominokit.jacksonapt.stream.impl.DefaultJsonReader Maven / Gradle / Ivy

The newest version!
/*
 * 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