Json.createParser: примеры (JAVA)
Json.createParser(String str): JsonParserОбщее описание
Метод Json.createParser в экосистеме Java относится к API JSON Processing (JSON-P, JSR 353). Он создаёт потоковый парсер, реализующий интерфейс javax.json.stream.JsonParser (в новых релизах пакет может быть jakarta.json.stream.JsonParser). Парсер использует модель событий (cursor/event) и подходит для чтения больших или поступающих по частям JSON-документов без загрузки всего содержимого в память.
Когда применяется
- При необходимости минимального потребления памяти при разборе больших JSON-файлов.
- При обработке потоков данных (сетевые потоки, файлы, стандартный ввод).
- Когда требуется пошаговый контроль над токенами (START_OBJECT, KEY_NAME, VALUE_STRING и т.д.).
Основные перегрузки и аргументы
Класс javax.json.Json обычно предоставляет две главные перегрузки:
Json.createParser(java.io.InputStream)- принимает входной байтовый поток. Кодировка должна определяться протоколом/контекстом; по умолчанию парсер ожидает UTF-8 при реализации, но рекомендуется оборачивать вInputStreamReaderпри явной кодировке.Json.createParser(java.io.Reader)- принимает символьный поток, напримерInputStreamReaderилиStringReader, когда кодировка уже учтена.
Возвращаемое значение
Обе перегрузки возвращают объект JsonParser. Его поведение:
hasNext()иnext()- итерация по событиям.getEvent()- тип текущего события (JsonParser.Event).- Специфичные методы для получения данных:
getString(),getInt(),getLong(),getBigDecimal()и т.д., доступные в зависимости от типа события. - Методы для получения местоположения (
getLocation()) и закрытия ресурса (close()).
Особенности поведения
- При встрече
KEY_NAMEследующая строка-значение может быть прочитана черезgetString()или ожидается соответствующее событие значения. - Некоторые реализации могут выдавать числовые значения как строки; рекомендуется использовать методы получения числовых типов после проверки события.
- Парсер строг к синтаксису: при ошибках выбрасывается
JsonParsingException.
Краткие примеры
Примеры показывают стандартные сценарии использования. Код и результат приведены последовательно.
1) Разбор из строки через StringReader
import javax.json.Json;
import javax.json.stream.JsonParser;
import java.io.StringReader;
String json = "{\"name\":\"Anna\",\"age\":30}";
try (JsonParser parser = Json.createParser(new StringReader(json))) {
while (parser.hasNext()) {
System.out.println(parser.next());
}
}
START_OBJECT KEY_NAME VALUE_STRING KEY_NAME VALUE_NUMBER END_OBJECT
2) Чтение ключей и значений в логике событий
try (JsonParser p = Json.createParser(new StringReader(json))) {
String key = null;
while (p.hasNext()) {
switch (p.next()) {
case KEY_NAME: key = p.getString(); break;
case VALUE_STRING: System.out.println(key + " = " + p.getString()); break;
case VALUE_NUMBER: System.out.println(key + " = " + p.getInt()); break;
default: break;
}
}
}
name = Anna age = 30
3) Парсинг массива
String arr = "[1, 2, 3]";
try (JsonParser p = Json.createParser(new StringReader(arr))) {
while (p.hasNext()) {
System.out.println(p.next() + (p.getEvent()==JsonParser.Event.VALUE_NUMBER ? (" -> " + p.getInt()) : ""));
}
}
START_ARRAY VALUE_NUMBER -> 1 VALUE_NUMBER -> 2 VALUE_NUMBER -> 3 END_ARRAY
Аналоги в Java и выбор между ними
Внутри Java-экосистемы доступны альтернативы парсеру событий.
- JsonReader (JSON-P) - объектная модель (DOM-подобно). Лучше при умеренных объёмах и потребности в удобной навигации через JsonObject/JsonArray.
- Jackson Streaming (com.fasterxml.jackson.core.JsonParser) - высокопроизводительный потоковый парсер с богатой функциональностью и широкой поддержкой. Подходит для критически быстрых задач.
- Gson JsonReader - потоковый парсер от Google, более простой API, удобен при интеграции с Gson.
Выбор зависит от требований: при необходимости стандартизованного API и интеграции с JSON-P удобен Json.createParser. При высоких требованиях к производительности или экосистемной совместимости предпочтение иногда отдаётся Jackson.
Аналоги в других языках и отличия
Краткие примеры для разных языков, показывающие соответствующие подходы.
PHP - json_decode
$json = '{"name":"Anna","age":30}';
$data = json_decode($json, true);
var_export($data);
array ( 'name' => 'Anna', 'age' => 30, )
JavaScript - JSON.parse
const obj = JSON.parse('{"name":"Anna","age":30}');
console.log(obj.name + ' ' + obj.age);
Anna 30
Python - json.loads и ijson (стриминг)
import json
s = '{"name":"Anna","age":30}'
print(json.loads(s)['name'])
# Для стриминга: ijson
# import ijson
# for prefix, event, value in ijson.parse(file): ...
Anna
SQL - JSON_VALUE / JSON_QUERY (напр., Oracle, SQL Server)
SELECT JSON_VALUE('{"name":"Anna","age":30}', '$.name') FROM DUAL;
ANNA
C# - System.Text.Json.Utf8JsonReader
using System.Text.Json;
var json = new ReadOnlySpan<byte>(Encoding.UTF8.GetBytes('{"name":"Anna","age":30}'));
var reader = new Utf8JsonReader(json);
while (reader.Read()) Console.WriteLine(reader.TokenType);
StartObject PropertyName String PropertyName Number EndObject
Go - encoding/json.Decoder (стриминг)
dec := json.NewDecoder(strings.NewReader('{"name":"Anna","age":30}'))
var m map[string]interface{}
dec.Decode(&m)
fmt.Println(m["name"])
Anna
Kotlin - тот же JSON-P или kotlinx.serialization / Jackson
Kotlin обычно использует те же библиотеки, что и Java; отличие - языковые обёртки и сопутствующие DSL.
Типичные ошибки и их проявления
1) NullPointerException при null-аргументе
Json.createParser((Reader) null);
java.lang.NullPointerException
at javax.json.Json.createParser(Json.java:...)
2) JsonParsingException при некорректном JSON
try (JsonParser p = Json.createParser(new StringReader("{name:}"))) {
while (p.hasNext()) p.next();
}
javax.json.stream.JsonParsingException: Unexpected char n ...
3) IllegalStateException при неверном вызове getXXX()
try (JsonParser p = Json.createParser(new StringReader('{"x":1}'))) {
p.next(); // START_OBJECT
System.out.println(p.getString());
}
java.lang.IllegalStateException: Not a string value
at ...
4) Ошибки кодировки
Использование InputStream без явного учета кодировки может привести к неверным символам. Рекомендуется оборачивать в InputStreamReader с указанием кодировки при сомнении.
Изменения в последних релизах
API JSON-P был определён в JSR 353 и включён в Java EE 7. Последующие изменения касались в основном расширения функциональности JSON-P (новые утилиты, JsonPatch/JsonMergePatch в версии 1.1) и перехода пакетов при миграции на Jakarta EE:
- JSON-P 1.0: исходный релиз с
Json.createParserи связанными типами. - JSON-P 1.1: добавлены дополнительные утилиты (JsonPatch и другие), но сигнатуры
createParserостались прежними. - При миграции на Jakarta EE (начиная с Jakarta 9) пакеты были переименованы из
javax.jsonвjakarta.json. Это основное изменение, влияющее на рефакторинг кода.
Расширенные и нестандартные примеры
1) Стриминг большого файла без загрузки в память
import javax.json.Json;
import javax.json.stream.JsonParser;
import java.io.FileInputStream;
try (FileInputStream fis = new FileInputStream("big.json");
JsonParser p = Json.createParser(fis)) {
while (p.hasNext()) {
JsonParser.Event e = p.next();
if (e == JsonParser.Event.KEY_NAME && "id".equals(p.getString())) {
// предполагается, что дальше идёт VALUE_NUMBER
p.next();
System.out.println("id = " + p.getLong());
}
}
}
(печать всех встреченных id по мере чтения без накопления всего документа)
2) Трансформация JSON на лету (копирование с переименованием ключа)
import javax.json.Json;
import javax.json.stream.JsonParser;
import javax.json.stream.JsonGenerator;
import java.io.StringReader;
import java.io.StringWriter;
String input = "{\"firstName\":\"Anna\",\"age\":30}";
StringWriter sw = new StringWriter();
try (JsonParser p = Json.createParser(new StringReader(input));
JsonGenerator g = Json.createGenerator(sw)) {
String key = null;
while (p.hasNext()) {
switch (p.next()) {
case START_OBJECT: g.writeStartObject(); break;
case END_OBJECT: g.writeEnd(); break;
case KEY_NAME: key = p.getString(); break;
case VALUE_STRING:
if ("firstName".equals(key)) g.write("name", p.getString());
else g.write(key, p.getString());
break;
case VALUE_NUMBER:
g.write(key, p.getInt());
break;
default: break;
}
}
}
System.out.println(sw.toString());
{"name":"Anna","age":30}
3) Отчёт об ошибке с указанием позиции
try (JsonParser p = Json.createParser(new StringReader("{\"x\": 1,\"y\": }"))) {
while (p.hasNext()) p.next();
} catch (javax.json.stream.JsonParsingException ex) {
System.out.println("Ошибка на позиции: " + ex.getLocation());
System.out.println(ex.getMessage());
}
Ошибка на позиции: LINE: 1, COLUMN: 14 Unexpected end of object
4) Комбинация с JsonReader для получения части дерева
// при встрече объекта можно собрать его в память для последующей удобной обработки
try (JsonParser p = Json.createParser(new StringReader('{"meta":{},"payload":{"a":1,"b":2}}'))) {
while (p.hasNext()) {
if (p.next() == JsonParser.Event.KEY_NAME && "payload".equals(p.getString())) {
p.next(); // START_OBJECT
// обёрнуть оставшуюся часть объекта в JsonReader не всегда тривиально с потоковым API,
// но можно читать события и строить JsonObject через JsonObjectBuilder
}
}
}
(пример показывает стратегию: смешивать стриминг и сборку поддеревьев при необходимости)