Skip to content

Миграция со стандартной библиотеки ​

cybergodev/json на 100% совместим со стандартной библиотекой encoding/json — достаточно заменить путь import, и существующий код компилируется и работает без каких-либо изменений (незначительные граничные различия из-за стандартной проверки ввода — в разделе Различия в поведении ниже). Эта страница поможет выполнить миграцию и узнать о дополнительных возможностях, доступных после неё.

Миграция в три шага ​

  1. Установка:

    bash
    go get github.com/cybergodev/json
  2. Замена import: замените "encoding/json" на "github.com/cybergodev/json".

    go
    // До миграции
    import "encoding/json"
    
    // После миграции
    import "github.com/cybergodev/json"
  3. Готово: компиляция проходит, весь существующий код не требует изменений.

Полностью совместимые API ​

В таблице ниже приведено соответствие encoding/json и cybergodev/json:

encoding/jsoncybergodev/jsonПримечание
Marshal(v)Marshal(v, cfg...)Совместимая сигнатура, дополнительный необязательный параметр cfg
Unmarshal(data, &v)Unmarshal(data, &v, cfg...)Аналогично
MarshalIndent(v, prefix, indent)То же имяПолная совместимость
Compact(dst, src)То же имяПолная совместимость
Indent(dst, src, prefix, indent)То же имяПолная совместимость
HTMLEscape(dst, src)То же имяПолная совместимость
Valid(data)Valid(data, cfg...)Совместимая сигнатура
NewEncoder(w)NewEncoder(w, cfg...)Совместимая сигнатура
NewDecoder(r)NewDecoder(r, cfg...)Совместимая сигнатура
NumberNumberСовместимый тип (String/Int64/Float64/MarshalJSON сохранены)
DelimDelimСовместимый тип (String() сохранён)
TokenTokenСовместимый тип

Совместимость Encoder и Decoder на уровне методов также полная — потоковый код после миграции не требует изменений:

МетодПринадлежностьСовместимость
Encode(v) / SetIndent(prefix, indent) / SetEscapeHTML(on)*EncoderПолная
Decode(v) / Token() / More() / Buffered() / InputOffset()*DecoderПолная
UseNumber() / DisallowUnknownFields()*DecoderПолная

Типы ошибок также соответствуют один к одному: код, полагающийся на errors.As / утверждение типа, работает напрямую — SyntaxError, UnmarshalTypeError, InvalidUnmarshalError, MarshalerError, UnsupportedTypeError, UnsupportedValueError существуют и ведут себя одинаково (подробнее см. Типы ошибок).

Поля локализации в структурах ошибок также сохранены по одному — код, полагающийся на них для локализации сбоев или классификационной статистики, не требует изменений:

ТипПолеТипОписание
SyntaxErrorOffsetint64Число байтов, прочитанных до возникновения ошибки
UnmarshalTypeErrorOffsetint64Число байтов, прочитанных до возникновения ошибки
UnmarshalTypeErrorStructstringИмя корневого типа, содержащего проблемное поле
UnmarshalTypeErrorFieldstringПолный путь от корневого узла до проблемного значения
UnsupportedValueErrorStrstringТекстовое представление неподдерживаемого значения (например, NaN, +Inf)

Когда Struct / Field у UnmarshalTypeError непусты, Error() выводит json: cannot unmarshal <value> into Go struct field <Struct>.<Field> of type <type> — дословно как в стандартной библиотеке.

Необязательный параметр cfg

Все дополнительные параметры cfg ...Config являются необязательными (variadic). Без них поведение на обычных данных совпадает со стандартной библиотекой (граничные различия стандартной проверки ввода — см. Различия в поведении ниже); передавайте их, только когда нужно включить режим безопасности, кэш и другие расширенные возможности.

Три намеренных исключения для cfg (из проектных соглашений библиотеки):

  • Вариадический параметр типизированных функций чтения (GetTyped, GetString, GetInt и др.) — это значение по умолчанию, а не cfg — Go допускает только один вариадический параметр. Для типизированного чтения с конфигурацией используйте SafeGet или типизированные методы чтения на Processor, созданном через New(cfg) (например, GetString, GetInt).
  • Удобные варианты (SetCreate, SetMultipleCreate, DeleteClean) эквивалентны обычным версиям с принудительно включёнными флагами CreatePaths (или CleanupNulls + CompactArrays).
  • Valid возвращает одиночный bool (сигнатура стандартной библиотеки); когда нужна причина сбоя, используйте ValidWithConfig (возвращает bool, error).

Пример кода: меняется только import ​

Пример ниже показывает результат замены «только import»: кодирование, декодирование и использование тегов структур (struct tag) полностью совпадают с encoding/json:

go
package main

import (
	"fmt"

	"github.com/cybergodev/json"
)

func main() {
	type User struct {
		Name string   `json:"name"`
		Age  int      `json:"age"`
		Tags []string `json:"tags"`
	}

	// Кодирование — в точности как в encoding/json
	user := User{Name: "Alice", Age: 30, Tags: []string{"go", "json"}}
	b, err := json.Marshal(user)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(b))
	// Вывод: {"name":"Alice","age":30,"tags":["go","json"]}

	// Декодирование — в точности как в encoding/json
	var u User
	if err := json.Unmarshal(b, &u); err != nil {
		panic(err)
	}
	fmt.Printf("%+v\n", u)
	// Вывод: {Name:Alice Age:30 Tags:[go json]}
}

Дополнительные возможности ​

После миграции, сохраняя совместимость со стандартной библиотекой, вы можете по мере необходимости использовать возможности, недоступные в stdlib:

ВозможностьПримерПодробнее
Запросы по путямjson.GetString(data, "user.name")Синтаксис выражений пути
Получение со значением по умолчаниюjson.GetInt(data, "timeout", 30)Функции запроса
Обобщённое получениеjson.GetTyped[User](data, "user")Обобщённые операции
Изменение по путиjson.Set(data, "user.name", "Bob")Операции изменения
Проверка по схемеjson.ValidateSchema(data, schema)Валидация Schema
Потоковый JSONLjson.StreamLinesInto[T](r, fn)Обработка JSONL
Высокопроизводительный процессорp, _ := json.New()Введение в Processor
Предразбор/предкомпиляция путейp.PreParse / p.CompilePathВведение в Processor
Параллельная итерацияjson.NewParallelIterator(items).ForEach(fn)Параллельная обработка
Отмена по контекстуjson.GetWithContext(ctx, data, path)Функции запроса
Глубокое сравнение JSONjson.CompareJSON(a, b)Вспомогательные инструменты
Хуки/аудит/замерыp.AddHook(json.LoggingHook(logger))Система хуков Hook
Режим безопасностиjson.SecurityConfig()Режим безопасности
Статистика/проверка здоровьяjson.GetStats() / json.GetHealthStatus()Введение в Processor

Различия в поведении ​

Для обычных данных поведение при конфигурации по умолчанию совпадает с encoding/json. Стоит помнить: cybergodev/json по умолчанию несёт слой проверки безопасности ввода (такова его позиция безопасной JSON-библиотеки) — ввод, выходящий за лимиты или содержащий опасные шаблоны, отклоняется, тогда как стандартная библиотека принимает всё. Различия сведены в таблицу:

Различиеencoding/jsonПоведение CyberGo по умолчаниюЕсли нужно другое поведение
Размер вводаБез ограниченийСвыше 100MB (MaxJSONSize) возвращает ErrSizeLimitУвеличить cfg.MaxJSONSize
Глубина вложенностиБез явного ограниченияСвыше 200 уровней возвращает ErrDepthLimitНастроить MaxNestingDepthSecurity
Опасные шаблоны содержимогоНе проверяются28 встроенных шаблонов блокируются по умолчанию (__proto__, <script, javascript:, eval(, onload и др.), возвращается ErrSecurityViolationПосле подтверждения доверия установить cfg.DisableDefaultPatterns = true (ключевые шаблоны вроде __proto__ всё равно блокируются)
Ширина контейнеровБез ограниченийОдин объект ≤ 100 000 ключей, один массив ≤ 100 000 элементовНастроить MaxObjectKeys / MaxArrayElements
Некорректный UTF-8Заменяется на U+FFFD при декодированииНемедленный отказ (ErrInvalidJSON)Заранее исправить кодировку ввода
BOM-префиксСинтаксическая ошибкаОтклоняется (ErrInvalidJSON)Предобработать и убрать BOM

Два пункта, которые часто трактуют неверно:

  1. Более информативные ошибки: JsonsError, возвращаемый при сбое операций с путями, содержит имя операции, путь и первопричину (поддерживает errors.Is/errors.As и Unwrap), но не меняет типы ошибок функций, совместимых со стандартной библиотекой (Unmarshal/Decode и др.) — они по-прежнему возвращают стандартные формы SyntaxError, UnmarshalTypeError и т. п.
  2. Проверка действует только на ввод: перечисленные ограничения касаются текстового ввода JSON (Unmarshal, Get, Parse, Valid и др.); при кодировании Go-значений через Marshal/Encode проверка содержимого не выполняется.

При работе с недоверенным вводом рекомендуется сразу использовать пресет json.SecurityConfig() (более строгие лимиты + полное сканирование), см. Режим безопасности.

FAQ по миграции ​

Вопрос: изменилось ли поведение json.Number с большими числами?

Нет. Decoder.UseNumber() соответствует стандартной библиотеке; поведение Number.Int64()/Float64() не изменилось. Когда нужно сохранить исходный текст числа, используйте json.Number как обычно.

Вопрос: совпадает ли поведение HTML-экранирования по умолчанию?

Да. Marshal/Encode по умолчанию экранируют <, >, & (как и стандартная библиотека); Encoder.SetEscapeHTML(false) отключает это — поведение и сигнатура совместимы.

Вопрос: а если существующий код использует пользовательские типы с json.Marshaler/json.Unmarshaler?

Полная совместимость. Оба интерфейса работают как обычно; пользовательские типы, реализующие их, ведут себя одинаково при кодировании/декодировании.

Вопрос: можно ли использовать новые возможности только в новом коде, не трогая старый?

Да, это и есть проектная цель. Функции уровня пакета кэшируют соответствующий Processor по «хвостовому параметру cfg» (см. соглашения по cfg выше); вызовы без cfg идут через глобальный процессор с конфигурацией по умолчанию — на обычных данных он совпадает со стандартной библиотекой, различия стандартной проверки ввода см. в таблице Различия в поведении выше.

Вопрос: Unmarshal отклоняет корректные данные со словами onload, eval( и т.п. — что делать?

Это стандартная проверка ввода блокирует шаблоны инъекций. После подтверждения доверия к вводу набор шаблонов по умолчанию можно отключить:

go
cfg := json.DefaultConfig()
cfg.DisableDefaultPatterns = true
err := json.Unmarshal(data, &v, cfg)

Обратите внимание: три ключевых шаблона __proto__, constructor[, prototype. блокируются всегда и не зависят от этого переключателя. Если нужно только дополнить правила, добавьте пользовательские шаблоны через AdditionalDangerousPatterns, не отключая набор по умолчанию.

Вопрос: документ больше 100MB отклоняется с ErrSizeLimit — как обработать?

Два пути: если действительно нужна обработка целиком — увеличьте cfg.MaxJSONSize; предпочтительнее перейти на потоковую обработку (NewStreamIterator / NewStreamObjectIterator для поэлементного чтения или семейство JSONL для построчной), избегая загрузки единым блоком в память, см. Обработка больших файлов.

Что дальше ​