Синтаксис выражений пути
Библиотека json поддерживает богатый синтаксис выражений пути для доступа и манипулирования любыми узлами JSON-данных.
Базовый синтаксис
Доступ к свойствам
Используйте точку . для доступа к свойствам объекта:
data := `{"user": {"name": "Alice", "age": 30}}`
name := json.GetString(data, "user.name") // "Alice"
age := json.GetInt(data, "user.age") // 30Вложенные пути
Используйте последовательные точки для доступа к глубоко вложенным свойствам:
data := `{
"company": {
"department": {
"team": {
"lead": "Bob"
}
}
}
}`
lead := json.GetString(data, "company.department.team.lead") // "Bob"Индексация массивов
Два синтаксиса для доступа к элементам массива:
data := `{"items": ["a", "b", "c", "d", "e"]}`
// Синтаксис 1: точка + индекс
first := json.GetString(data, "items.0") // "a"
// Синтаксис 2: квадратные скобки + индекс
first2 := json.GetString(data, "items[0]") // "a"Отрицательные индексы
Отрицательные индексы отсчитываются с конца массива, -1 означает последний элемент:
data := `{"items": ["a", "b", "c", "d", "e"]}`
val := json.GetString(data, "items[-1]") // "e" (последний)
val = json.GetString(data, "items[-2]") // "d" (предпоследний)
val = json.GetString(data, "items[-5]") // "a" (эквивалентно [0])| Индекс | Значение | Эквивалентный положительный индекс |
|---|---|---|
[0] | Первый элемент | — |
[1] | Второй элемент | — |
[-1] | Последний элемент | [len-1] |
[-2] | Предпоследний элемент | [len-2] |
[-N] | N-й с конца | [len-N] |
Многомерные массивы
Используйте последовательные индексы для доступа к вложенным массивам:
data := `{"matrix": [[1, 2, 3], [4, 5, 6], [7, 8, 9]]}`
val := json.GetInt(data, "matrix[0][0]") // 1
val = json.GetInt(data, "matrix[1][2]") // 6
val = json.GetInt(data, "matrix[-1][-1]") // 9Поведение при выходе за границы
Выход за границы индекса не вызывает panic. Типобезопасные функции получения (GetString, GetInt и т.д.) возвращают нулевые значения, а функция Get возвращает ошибку:
data := `{"items": ["a", "b", "c"]}`
// Положительный индекс за границами -> возвращается нулевое значение без ошибки
json.GetString(data, "items[10]") // "" (пустая строка)
json.GetInt(data, "items[10]") // 0
json.Get(data, "items[10]") // nil, ErrPathNotFound
// Отрицательный индекс за границами -> также возвращается нулевое значение
json.GetString(data, "items[-10]") // "" (пустая строка)
json.GetInt(data, "items[-10]") // 0| Функция | Возвращаемое значение при выходе за границы |
|---|---|
Get | (nil, ErrPathNotFound) |
GetString | "" |
GetInt | 0 |
GetFloat | 0.0 |
GetBool | false |
GetArray | nil |
Границы индексов
- Положительные индексы должны быть в диапазоне
[0, len), отрицательные индексы после преобразования (len + index) — аналогично - Доступ за границами возвращает нулевое значение соответствующего типа, не вызывает panic и не возвращает ошибку
- Если нужно проверить существование пути, используйте
Getи проверьте, равна ли ошибкаjson.ErrPathNotFound
Расширенный синтаксис
Срезы массивов [start:end:step]
Извлечение подмассива с использованием синтаксиса срезов в стиле Python [start:end:step]. Все три параметра можно опустить:
| Параметр | Описание | Значение по умолчанию при опускании |
|---|---|---|
start | Начальный индекс (включительно) | 0 (при положительном шаге) или len-1 (при отрицательном шаге) |
end | Конечный индекс (исключительно) | len (при положительном шаге) или -1 (при отрицательном шаге) |
step | Шаг | 1 |
Шпаргалка по синтаксису срезов
| Синтаксис | Значение | Пример ([0,1,2,3,4]) | Результат |
|---|---|---|---|
[:] | Полная копия | [0,1,2,3,4][:] | [0,1,2,3,4] |
[N:] | От N до конца | [0,1,2,3,4][2:] | [2,3,4] |
[:N] | От начала до N | [0,1,2,3,4][:3] | [0,1,2] |
[N:M] | От N до M-1 | [0,1,2,3,4][1:4] | [1,2,3] |
[::S] | Каждый S-й элемент | [0,1,2,3,4][::2] | [0,2,4] |
[N::S] | От N с шагом S | [0,1,2,3,4][1::2] | [1,3] |
[:M:S] | От начала до M с шагом S | [0,1,2,3,4][:4:2] | [0,2] |
[N:M:S] | Полный формат с тремя параметрами | [0,1,2,3,4][0:5:2] | [0,2,4] |
[::-1] | Разворот массива | [0,1,2,3,4][::-1] | [4,3,2,1,0] |
[::-S] | Обратный шаг | [0,1,2,3,4][::-2] | [4,2,0] |
Прямые срезы
data := `{"numbers": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}`
// Базовый срез
slice := json.GetArray(data, "numbers[2:5]") // [2, 3, 4]
// Без start (с начала)
slice2 := json.GetArray(data, "numbers[:3]") // [0, 1, 2]
// Без end (до конца)
slice3 := json.GetArray(data, "numbers[7:]") // [7, 8, 9]
// Шаг 2 (элементы на чётных позициях)
slice4 := json.GetArray(data, "numbers[::2]") // [0, 2, 4, 6, 8]
// Полный набор параметров
slice5 := json.GetArray(data, "numbers[1:8:3]") // [1, 4, 7]
// Полная копия
slice6 := json.GetArray(data, "numbers[:]") // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]Срезы с отрицательными индексами
Параметры start и end срезов поддерживают отрицательные индексы:
data := `{"numbers": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}`
// Последние 3 элемента
json.GetArray(data, "numbers[-3:]") // [7, 8, 9]
// Все кроме последних 2 элементов
json.GetArray(data, "numbers[:-2]") // [0, 1, 2, 3, 4, 5, 6, 7]
// С 5-го с конца до 2-го с конца
json.GetArray(data, "numbers[-5:-2]") // [5, 6, 7]
// С индекса 2 до предпоследнего (не включая последний)
json.GetArray(data, "numbers[2:-1]") // [2, 3, 4, 5, 6, 7, 8]Обратные срезы
Отрицательный шаг обеспечивает обратный обход:
data := `{"letters": ["a", "b", "c", "d", "e"]}`
// Разворот массива
json.GetArray(data, "letters[::-1]") // ["e", "d", "c", "b", "a"]
// Обратный шаг 2
json.GetArray(data, "letters[::-2]") // ["e", "c", "a"]
// С индекса 3 до 1 (обратный порядок)
json.GetArray(data, "letters[3:1:-1]") // ["d", "c"]
// Последние 3 элемента в обратном порядке
json.GetArray(data, "letters[2::-1]") // ["c", "b", "a"]Поведение при выходе за границы
Срезы автоматически обрезают выходящие за границы индексы (clamp) и не возвращают ошибку:
data := `{"items": [0, 1, 2]}`
// Выходящий за границы start/end будет автоматически обрезан до допустимого диапазона
json.GetArray(data, "items[0:100]") // [0, 1, 2] (end обрезан до len=3)
json.GetArray(data, "items[10:20]") // [] (start >= end, пустой результат)
// При start >= end возвращается пустой массив
json.GetArray(data, "items[2:2]") // []
json.GetArray(data, "items[3:1]") // []Разница обработки границ для срезов и индексов
- Выход индекса за границы (например,
items[10]) возвращает нулевое значение соответствующего типа без ошибки - Выход среза за границы (например,
items[10:20]) автоматически обрезается и возвращает пустой массив без ошибки
Извлечение полей {field1,field2}
Извлечение только определённых полей из объекта:
data := `{
"user": {
"id": 1001,
"name": "Alice",
"email": "[email protected]",
"password": "secret",
"age": 25
}
}`
// Извлечь только id и name
extracted, _ := json.Get(data, "user{id,name}")
// Результат: {"id": 1001, "name": "Alice"}Плоское извлечение {flat:field}
При извлечении значений из полей объектов массива, если само поле является массивом, обычное извлечение создаёт вложенный массив. Использование префикса {flat:} рекурсивно разворачивает все вложенные массивы, создавая плоский результирующий массив.
Обычное извлечение vs плоское извлечение
data := `{
"groups": [
{"tags": ["go", "json"]},
{"tags": ["python", "yaml"]}
]
}`
// Обычное извлечение -> вложенный массив
json.GetArray(data, "groups{tags}")
// [["go", "json"], ["python", "yaml"]]
// Плоское извлечение -> развёрнуто в одномерный массив
json.GetArray(data, "groups{flat:tags}")
// ["go", "json", "python", "yaml"]Цепочечное плоское извлечение
Для многоуровневых вложенных массивов можно последовательно использовать {flat:} для послойного разворачивания:
data := `{
"departments": [
{
"teams": [
{"members": [{"name": "Alice"}, {"name": "Bob"}]}
]
},
{
"teams": [
{"members": [{"name": "Carol"}]}
]
}
]
}`
// Трёхуровневое выравнивание: departments -> teams -> members -> name
json.GetArray(data, "departments{flat:teams}{flat:members}{name}")
// ["Alice", "Bob", "Carol"]Плоское извлечение с последующими операциями
Результат плоского извлечения может использоваться дальше со срезами, индексами и другими операциями:
data := `{
"orders": [
{"items": ["book", "pen"]},
{"items": ["laptop", "mouse", "keyboard"]},
{"items": ["cup"]}
]
}`
// Срез после выравнивания
json.GetArray(data, "orders{flat:items}[0:3]")
// ["book", "pen", "laptop"]Ограничения
- При извлечении нескольких полей
{flat:field1,field2}флагflatне действует, поскольку извлечение нескольких полей создаёт объект, а не массив - Выравнивание рекурсивно разворачивает вложенные массивы на всех уровнях, а не только на первом
Операция добавления [+]
Добавление элемента в конец массива:
data := `{"items": [1, 2, 3]}`
updated, _ := json.Set(data, "items[+]", 4)
// Результат: {"items": [1, 2, 3, 4]}
updated, _ = json.Set(updated, "items[+]", 5)
// Результат: {"items": [1, 2, 3, 4, 5]}Подстановочный знак [*]
data := `{"items": [1, 2, 3]}`
updated, _ := json.Set(data, "items[*]", 0)
// Результат: {"items": [0, 0, 0]}Валидация путей
Валидация пути через Processor
Используйте Processor.CompilePath для проверки корректности формата пути:
p, err := json.New()
if err != nil {
panic(err)
}
// Компиляция пути (автоматическая проверка формата)
cp, err := p.CompilePath("user.profile.name")
if err != nil {
fmt.Println("Некорректный путь:", err)
}
cp, err = p.CompilePath("items[0:10:2]")
if err != nil {
fmt.Println("Некорректный путь:", err)
}Специальные пути
Корневой путь
Пустая строка "" или "." обозначает корень:
data := `{"name": "test"}`
// Получение всего объекта
root, _ := json.Get(data, "") // {"name": "test"}
root, _ = json.Get(data, ".") // АналогичноЭкранирование путей
Если имя ключа содержит специальные символы, используйте экранирование:
data := `{"user.name": "Alice"}`
// Имя ключа, содержащее точку
name := json.GetString(data, "user\\.name") // "Alice"Типы сегментов пути
Внутренне библиотека разбирает путь на сегменты различных типов (это детали внутренней реализации, не экспортируемые как публичный API):
| Тип | Пример синтаксиса | Описание |
|---|---|---|
| Доступ к свойству | user.name | Доступ к свойству объекта |
| Индекс массива | items[0] | Доступ к элементу массива |
| Срез массива | items[1:5] | Доступ к диапазону среза |
| Подстановочный знак | items[*] | Соответствие всем элементам |
| Извлечение полей | {name,email} | Извлечение нескольких полей |
| Плоское извлечение | {flat:tags} | Извлечение с рекурсивным разворачиванием вложенных массивов |
| Операция добавления | items[+] | Добавление элемента в массив |
Полный пример
package main
import (
"fmt"
"github.com/cybergodev/json"
)
func main() {
data := `{
"store": {
"books": [
{"title": "Go 101", "price": 25, "category": "programming"},
{"title": "JSON Guide", "price": 35, "category": "programming"},
{"title": "Clean Code", "price": 45, "category": "programming"}
],
"prices": [10, 20, 30, 40, 50]
}
}`
// 1. Базовый доступ
title := json.GetString(data, "store.books.0.title")
fmt.Println("Первая книга:", title)
// 2. Срез массива
books := json.GetArray(data, "store.books[0:2]")
fmt.Printf("Первые 2 книги: %d элементов\n", len(books))
// 3. Срез с шагом
prices := json.GetArray(data, "store.prices[::2]")
fmt.Println("\nКаждая вторая цена:", prices)
// 4. Извлечение полей
extracted, _ := json.Get(data, "store.books[0]{title,price}")
fmt.Println("\nИзвлечённые поля:", extracted)
// 5. Добавление элемента
updated, _ := json.Set(data, "store.books[+]", map[string]any{
"title": "New Book",
"price": 55,
"category": "programming",
})
fmt.Println("\nПосле добавления:", json.Valid([]byte(updated)))
}Что дальше
- Документация API — полный справочник API
- Примеры использования — больше практических примеров