Skip to content

Файлы конфигурации ​

В этом руководстве показано, как обрабатывать типичные сценарии работы с файлами конфигурации в CyberGo JSON: загрузка, чтение вложенных значений, изменение, сохранение и слияние конфигурации по умолчанию с пользовательской.

Полный жизненный цикл конфигурации ​

Загрузка конфигурации → чтение вложенных значений → изменение → сохранение обратно в файл → перезагрузка для проверки. В примере используется временный файл, чтобы он работал автономно.

go
package main

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/cybergodev/json"
)

func main() {
	// Используем временный каталог, чтобы пример работал автономно
	tmpDir, err := os.MkdirTemp("", "cybergo-config-*")
	if err != nil {
		panic(err)
	}
	defer os.RemoveAll(tmpDir)

	configPath := filepath.Join(tmpDir, "config.json")

	// Записываем исходный файл конфигурации
	initial := `{
        "server": {"host": "0.0.0.0", "port": 8080},
        "database": {"host": "localhost", "port": 5432, "name": "appdb"},
        "logging": {"level": "info"}
    }`
	if err := os.WriteFile(configPath, []byte(initial), 0644); err != nil {
		panic(err)
	}

	// 1. Загружаем конфигурацию из файла
	data, err := json.LoadFromFile(configPath)
	if err != nil {
		panic(err)
	}

	// 2. Читаем вложенные значения (поддерживается необязательный аргумент по умолчанию)
	fmt.Printf("Сервер: %s:%d\n", json.GetString(data, "server.host"), json.GetInt(data, "server.port"))
	fmt.Printf("База данных: %s/%s\n", json.GetString(data, "database.host"), json.GetString(data, "database.name"))
	fmt.Printf("Уровень логирования: %s\n", json.GetString(data, "logging.level", "info"))

	// 3. Изменяем конфигурацию (обновляем существующие значения)
	data, err = json.Set(data, "server.port", 9090)
	if err != nil {
		panic(err)
	}
	data, err = json.Set(data, "logging.level", "debug")
	if err != nil {
		panic(err)
	}

	// 4. Сохраняем обратно в файл (с форматированием)
	if err := json.SaveToFile(configPath, data, json.PrettyConfig()); err != nil {
		panic(err)
	}

	// 5. Перезагружаем, чтобы проверить сохранение изменений
	reloaded, err := json.LoadFromFile(configPath)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Порт после перезапуска: %d\n", json.GetInt(reloaded, "server.port"))
	fmt.Printf("Лог после перезапуска: %s\n", json.GetString(reloaded, "logging.level"))
}

Слияние конфигурации по умолчанию и пользовательской ​

В реальных приложениях часто нужно наложить пользовательскую конфигурацию поверх встроенных значений по умолчанию, а затем дополнить недостающие вложенные пути. MergeJSON выполняет глубокое слияние (пользовательские значения имеют приоритет), а SetCreate автоматически создаёт промежуточные пути, которых ещё нет.

go
package main

import (
	"fmt"

	"github.com/cybergodev/json"
)

func main() {
	// Встроенная конфигурация по умолчанию
	defaults := `{
        "server": {"host": "0.0.0.0", "port": 8080, "timeout": 30},
        "database": {"host": "localhost", "port": 5432, "pool": 10},
        "logging": {"level": "info", "format": "json"}
    }`

	// Пользовательская конфигурация (переопределяет часть полей)
	userConfig := `{
        "server": {"port": 3000},
        "database": {"host": "db.prod.example.com"},
        "logging": {"level": "debug"}
    }`

	// Глубокое слияние: пользовательская конфигурация переопределяет умолчания, непереопределённые поля сохраняются
	merged, err := json.MergeJSON(defaults, userConfig)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Порт: %d (переопределён пользователем)\n", json.GetInt(merged, "server.port"))
	fmt.Printf("Таймаут: %d (значение по умолчанию)\n", json.GetInt(merged, "server.timeout"))
	fmt.Printf("База данных: %s:%d\n", json.GetString(merged, "database.host"), json.GetInt(merged, "database.port"))

	// SetCreate добавляет вложенные пути, которых ещё нет (создаёт промежуточные объекты)
	merged, err = json.SetCreate(merged, "features.metrics.enabled", true)
	if err != nil {
		panic(err)
	}
	merged, err = json.SetCreate(merged, "features.metrics.endpoint", "/metrics")
	if err != nil {
		panic(err)
	}

	fmt.Printf("Метрики включены: %v\n", json.GetBool(merged, "features.metrics.enabled"))
	fmt.Printf("Адрес метрик: %s\n", json.GetString(merged, "features.metrics.endpoint"))
}

// Вывод:
// Порт: 3000 (переопределён пользователем)
// Таймаут: 30 (значение по умолчанию)
// База данных: db.prod.example.com:5432
// Метрики включены: true
// Адрес метрик: /metrics

Подсказка

MergeJSON выполняет глубокое рекурсивное слияние: ключи объектов объединяются слой за слоем, а массивы и скалярные значения заменяются напрямую. Чтобы объединить несколько источников конфигурации за раз, используйте MergeMany([]string{...}).

Выбор режима слияния: MergeUnion / MergeIntersection / MergeDifference ​

Config.MergeMode управляет стратегией слияния; по умолчанию используется MergeUnion (объединение, при конфликте побеждает пользовательское значение). Два других режима служат разным задачам управления конфигурацией:

РежимСемантикаТипичные сценарии конфигурации
MergeUnion (по умолчанию)Полное объединение двух объектов; при конфликте побеждает пользовательское значениеОбычное наслаивание конфигурации: база из умолчаний + пользовательские переопределения
MergeIntersectionСохраняются только ключи, присутствующие с обеих сторон (значения берутся из пользовательской стороны)Извлечение «какие общие настройки пользователь фактически переопределил»; пересечение конфигураций нескольких окружений
MergeDifferenceСохраняются только ключи, уникальные для базовой стороны (первый аргумент)Поиск «какие параметры всё ещё используют значения по умолчанию» — для аудита или генерации документации по конфигурации
go
package main

import (
	"fmt"

	"github.com/cybergodev/json"
)

func main() {
	// Те же умолчания и пользовательская конфигурация, что и в предыдущем разделе
	defaults := `{
        "server": {"host": "0.0.0.0", "port": 8080, "timeout": 30},
        "database": {"host": "localhost", "port": 5432, "pool": 10},
        "logging": {"level": "info", "format": "json"}
    }`
	user := `{
        "server": {"port": 3000},
        "database": {"host": "db.prod.example.com"},
        "logging": {"level": "debug"}
    }`

	// Пересечение: остаются только ключи, встречающиеся с обеих сторон
	// (вложенные объекты пересекаются рекурсивно, скаляры берутся из пользовательской стороны)
	interCfg := json.DefaultConfig()
	interCfg.MergeMode = json.MergeIntersection
	overridden, err := json.MergeJSON(defaults, user, interCfg)
	if err != nil {
		panic(err)
	}
	fmt.Println("[Пересечение] порт, переопределённый пользователем:", json.GetInt(overridden, "server.port"))
	fmt.Println("[Пересечение] хост БД, переопределённый пользователем:", json.GetString(overridden, "database.host"))
	// Вывод:
	// [Пересечение] порт, переопределённый пользователем: 3000
	// [Пересечение] хост БД, переопределённый пользователем: db.prod.example.com

	// Разность: остаются только ключи из умолчаний, не переопределённые пользователем
	diffCfg := json.DefaultConfig()
	diffCfg.MergeMode = json.MergeDifference
	untouched, err := json.MergeJSON(defaults, user, diffCfg)
	if err != nil {
		panic(err)
	}
	fmt.Println("[Разность] хост со значением по умолчанию:", json.GetString(untouched, "server.host"))
	fmt.Println("[Разность] таймаут со значением по умолчанию:", json.GetInt(untouched, "server.timeout"))
	fmt.Println("[Разность] пул соединений со значением по умолчанию:", json.GetInt(untouched, "database.pool"))
	// Вывод:
	// [Разность] хост со значением по умолчанию: 0.0.0.0
	// [Разность] таймаут со значением по умолчанию: 30
	// [Разность] пул соединений со значением по умолчанию: 10
}

Подсказка

MergeMany также читает cfg.MergeMode (слияние источников по одному слева направо). Полный эффект режима объединения показан в предыдущем разделе; оба режима, отличных от умолчания, смотрят на данные «с первого аргумента как базы»: пересечение отвечает на вопрос «что изменено», разность — «что ещё не изменено».

Следующие шаги ​