---
sidebar_label: "Синтаксис путей"
title: "Синтаксис пути - CyberGo JSON | JSONPath"
description: "Руководство по путям CyberGo JSON: user.name, items[0], срезы, подстановочные знаки и извлечение полей для узлов JSON в Go."
sidebar_position: 2
---

# Синтаксис выражений пути

Библиотека json поддерживает богатый синтаксис выражений пути для доступа и манипулирования любыми узлами JSON-данных.

## Базовый синтаксис

### Доступ к свойствам

Используйте точку `.` для доступа к свойствам объекта:

```go
data := `{"user": {"name": "Alice", "age": 30}}`

name := json.GetString(data, "user.name")    // "Alice"
age := json.GetInt(data, "user.age")         // 30
```

### Вложенные пути

Используйте последовательные точки для доступа к глубоко вложенным свойствам:

```go
data := `{
    "company": {
        "department": {
            "team": {
                "lead": "Bob"
            }
        }
    }
}`

lead := json.GetString(data, "company.department.team.lead")  // "Bob"
```

### Индексация массивов

Два синтаксиса для доступа к элементам массива:

```go
data := `{"items": ["a", "b", "c", "d", "e"]}`

// Синтаксис 1: точка + индекс
first := json.GetString(data, "items.0")   // "a"

// Синтаксис 2: квадратные скобки + индекс
first2 := json.GetString(data, "items[0]")   // "a"
```

#### Отрицательные индексы

Отрицательные индексы отсчитываются с конца массива, `-1` означает последний элемент:

```go
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]` |

#### Многомерные массивы

Используйте последовательные индексы для доступа к вложенным массивам:

```go
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 возвращает ошибку:

```go
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` |

::: tip Границы индексов
- Положительные индексы должны быть в диапазоне `[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]` |

#### Прямые срезы

```go
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` срезов поддерживают отрицательные индексы:

```go
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]
```

#### Обратные срезы

Отрицательный шаг обеспечивает обратный обход:

```go
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) и не возвращают ошибку:

```go
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]")     // []
```

::: warning Разница обработки границ для срезов и индексов
- **Выход индекса за границы** (например, `items[10]`) возвращает нулевое значение соответствующего типа без ошибки
- **Выход среза за границы** (например, `items[10:20]`) автоматически обрезается и возвращает пустой массив без ошибки
:::

### Извлечение полей `{field1,field2}`

Извлечение только определённых полей из объекта:

```go
data := `{
    "user": {
        "id": 1001,
        "name": "Alice",
        "email": "alice@example.com",
        "password": "secret",
        "age": 25
    }
}`

// Извлечь только id и name
extracted, _ := json.Get(data, "user{id,name}")
// Результат: {"id": 1001, "name": "Alice"}
```

### Плоское извлечение `{flat:field}`

При извлечении значений из полей объектов массива, если само поле является массивом, обычное извлечение создаёт вложенный массив. Использование префикса `{flat:}` рекурсивно разворачивает все вложенные массивы, создавая плоский результирующий массив.

#### Обычное извлечение vs плоское извлечение

```go
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:}` для послойного разворачивания:

```go
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"]
```

#### Плоское извлечение с последующими операциями

Результат плоского извлечения может использоваться дальше со срезами, индексами и другими операциями:

```go
data := `{
    "orders": [
        {"items": ["book", "pen"]},
        {"items": ["laptop", "mouse", "keyboard"]},
        {"items": ["cup"]}
    ]
}`

// Срез после выравнивания
json.GetArray(data, "orders{flat:items}[0:3]")
// ["book", "pen", "laptop"]
```

::: info Ограничения
- При извлечении нескольких полей `{flat:field1,field2}` флаг `flat` не действует, поскольку извлечение нескольких полей создаёт объект, а не массив
- Выравнивание рекурсивно разворачивает вложенные массивы на всех уровнях, а не только на первом
:::

### Операция добавления `[+]`

Добавление элемента в конец массива:

```go
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]}
```

### Подстановочный знак `[*]`

```go
data := `{"items": [1, 2, 3]}`

updated, _ := json.Set(data, "items[*]", 0)
// Результат: {"items": [0, 0, 0]}
```

---

## Валидация путей

### Валидация пути через Processor

Используйте `Processor.CompilePath` для проверки корректности формата пути:

```go
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)
}
```

---

## Специальные пути

### Корневой путь

Пустая строка `""` или `"."` обозначает корень:

```go
data := `{"name": "test"}`

// Получение всего объекта
root, _ := json.Get(data, "")     // {"name": "test"}
root, _ = json.Get(data, ".")     // Аналогично
```

### Экранирование путей

Если имя ключа содержит специальные символы, используйте экранирование:

```go
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[+]` | Добавление элемента в массив |

---

## Полный пример

```go
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-reference/) — полный справочник API
- [Примеры использования](../examples/) — больше практических примеров
