Json.createParser: примеры (JAVA)

Работа с Json.createParser
Раздел: Работа с JSON (Jackson, Gson, JSON-P)
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) Стриминг большого файла без загрузки в память

Пример java
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 на лету (копирование с переименованием ключа)

Пример java
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) Отчёт об ошибке с указанием позиции

Пример java
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 для получения части дерева

Пример java
// при встрече объекта можно собрать его в память для последующей удобной обработки
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
        }
    }
}
(пример показывает стратегию: смешивать стриминг и сборку поддеревьев при необходимости)

джава Json.createParser function comments

En
Json.createParser Creates a JSON parser from a string