Пакет json
Обзор
Пакет json реализует семантическую обработку JSON согласно RFC 8259. JSON — простой формат обмена данными, который может представлять примитивные типы данных, такие как логические значения, строки и числа, а также структурированные типы данных, такие как объекты и массивы.
Введение в пакет см. в руководстве Работа с JSON.
Marshal и Unmarshal кодируют и декодируют значения Go в текст JSON, содержащийся в []byte, и обратно. MarshalWrite и UnmarshalRead работают с текстом JSON, записывая его в io.Writer или считывая из io.Reader. MarshalEncode и UnmarshalDecode работают с текстом JSON, кодируя его в jsontext.Encoder или декодируя из jsontext.Decoder. Функциям кодирования и декодирования можно передавать Options для настройки семантического поведения маршалинга и демаршалинга (то есть изменения способа интерпретации данных JSON как данных Go и наоборот). Функциям кодирования и декодирования также можно передавать jsontext.Options для настройки синтаксического поведения кодирования или декодирования.
Типы данных JSON сопоставляются с типами данных Go и обратно на основе наиболее близкого логического соответствия между двумя системами типов. Например, логическое значение JSON соответствует bool в Go, строка JSON — строке Go, число JSON — типу int, uint или float в Go, массив JSON — срезу или массиву Go, а объект JSON — структуре или карте Go. Полный список соответствий между системами типов JSON и Go см. в документации к Marshal и Unmarshal.
Произвольные типы Go могут настраивать свое представление в JSON, реализуя Marshaler, MarshalerTo, Unmarshaler или UnmarshalerFrom. Это позволяет авторам типов Go контролировать сериализацию этих типов в JSON. В качестве альтернативы пользователи могут реализовать функции, соответствующие MarshalFunc, MarshalToFunc, UnmarshalFunc или UnmarshalFromFunc, чтобы задать представление произвольных типов в JSON. Это позволяет вызывающим код JSON-функциональности контролировать сериализацию любых произвольных типов в JSON.
Представление структур Go в JSON
Структура Go естественным образом представляется объектом JSON, где каждому полю структуры Go соответствует элемент объекта JSON. При маршалинге все поля структуры Go рекурсивно кодируются в элементы объекта JSON в порядке обхода в глубину, за исключением игнорируемых или пропускаемых полей. При демаршалинге элементы объекта JSON рекурсивно декодируются в соответствующие поля структуры Go. Элементы объекта, которым не соответствуют поля структуры, также называемые «неизвестными элементами», по умолчанию игнорируются или отклоняются, если указан параметр RejectUnknownMembers.
Представление каждого поля структуры можно настроить с помощью тега поля структуры "json", в котором параметры перечисляются через запятую. В особом случае, если весь тег имеет вид `json:"-"`, поле игнорируется при формировании его представления в JSON. Некоторые параметры также можно задать с помощью передаваемого вызывающим кодом Options. Параметры, указанные для поля, имеют приоритет над параметрами, заданными вызывающим кодом.
Первый параметр задает переопределение имени объекта JSON для поля структуры Go. Если имя не указано, в качестве имени объекта JSON используется имя поля структуры Go. По умолчанию при демаршалинге для определения поля структуры Go, соответствующего имени объекта JSON, используется сопоставление с учетом регистра.
После имени поддерживаются следующие параметры тега:
omitzero: При маршалинге параметр "omitzero" указывает, что поле структуры следует пропустить, если его значение является нулевым — это определяется методом "IsZero() bool", если он имеется, а в противном случае — тем, является ли значение нулевым значением Go для данного типа. Этот параметр не влияет на демаршалинг.
omitempty: При маршалинге параметр "omitempty" указывает, что поле структуры следует пропустить, если его значение кодировалось бы как null, пустая строка, пустой объект или пустой массив JSON. Этот параметр не влияет на демаршалинг.
string: Параметр "string" указывает, что при маршалинге или демаршалинге значения поля структуры следует включить StringifyNumbers. В результате типы, которые обычно кодируются как число JSON, вместо этого кодируются как число JSON в кавычках внутри строки JSON, а декодируются из строки JSON, содержащей число JSON без окружающих пробелов. Параметр "string" применяется только к верхнему уровню значения поля структуры Go. Ошибка возникает при применении этого параметра к типу, который не кодируется как число JSON. Обратите внимание: составные типы, такие как массивы, срезы, структуры и карты, не кодируются как числа JSON, поэтому применение этого параметра приведет к ошибке, а не повлияет на числа JSON внутри таких типов. Этот дополнительный уровень кодирования часто необходим, поскольку многие парсеры JSON не могут точно представлять 64-битные целые числа.
case: При демаршалинге параметр "case" задает способ сопоставления имен объектов JSON с именами полей структуры Go в JSON. Параметр задается парой ключ-значение в формате "case:value", где значением должно быть 'ignore' или 'strict'. Значение 'ignore' задает сопоставление без учета регистра, а также игнорирование дефисов и подчеркиваний. Если совпадают несколько полей, выбирается поле с точным совпадением имени; в противном случае сообщается об ошибке, поскольку выбор поля для демаршалинга неоднозначен. Значение 'strict' задает сопоставление с учетом регистра. Этот параметр имеет приоритет над параметром MatchCaseInsensitiveNames.
embed: Параметр "embed" указывает, что содержимое данного типа поля, представимое в JSON, должно продвигаться так, как если бы оно было указано в родительской структуре. Это эквивалент в JSON встраивания структур Go. Поле Go, встроенное в структуру, неявно встраивается и в JSON, если явно не задано имя JSON. Встроенное поле должно быть структурой Go (не реализующей методы JSON), jsontext.Value, map[~string]T или неименованным указателем на такие типы. При маршалинге встроенные поля указательного типа пропускаются, если указатель равен nil. Встроенные поля типа jsontext.Value и map[~string]T называются «резервными встроенными полями», поскольку они могут представлять все возможные элементы объекта JSON, которые непосредственно не обрабатываются родительской структурой. В структуре можно указать только одно резервное встроенное поле, но обычных встроенных полей может быть несколько. Этот параметр нельзя указывать вместе с любыми другими параметрами (включая имя JSON).
Параметры "omitzero" и "omitempty" ведут себя сходным образом. Первый определяется в терминах системы типов Go, а второй — в терминах системы типов JSON. Поэтому в некоторых случаях они работают по-разному. Например, при использовании "omitzero" пропускается только nil-срез или nil-карта, а при использовании "omitempty" пропускается пустой срез или карта независимо от того, равны ли они nil. Параметр "omitzero" полезен для типов с хорошо определенным нулевым значением (например, net/netip.Addr) или с методом IsZero (например, time.Time.IsZero).
Каждой структуре Go соответствует список полей, представимых в JSON. Он строится путем поиска в ширину по всем полям структуры (за исключением неэкспортируемых и игнорируемых полей), при котором поиск рекурсивно продолжается во встроенных структурах. Набор невстроенных полей структуры должен содержать уникальные имена JSON. Если несколько полей имеют одинаковое имя JSON, приоритет получает поле с наименьшей глубиной, а остальные поля на большей глубине исключаются из списка полей, представимых в JSON. Если несколько полей на наименьшей глубине имеют одинаковое имя JSON, но только одно из них явно снабжено тегом с именем JSON, приоритет получает это поле, а все остальные исключаются из списка. Это аналогично правилам видимости Go при выборе полей структуры со встроенными типами структур.
Маршалинг или демаршалинг непустой структуры, не содержащей полей, представимых в JSON, приводит к ошибке SemanticError. Неэкспортируемые поля не должны иметь тегов `json`, кроме `json:"-"`.
Вопросы безопасности
JSON часто используется как формат обмена данными для взаимодействия между разными системами, которые могут быть реализованы на разных языках. Для обеспечения совместимости и безопасности важно, чтобы все реализации одинаково трактовали семантический смысл данных.
Например, предположим, что у нас есть два микросервиса. Первый сервис отвечает за аутентификацию запроса JSON, а второй — за выполнение запроса, исходя из того, что он прошел аутентификацию. Если злоумышленник сможет создать запрос JSON таким образом, что оба сервиса сочтут, будто один и тот же запрос поступил от разных пользователей, он сможет обойти проверку подлинности, используя действительные учетные данные одного пользователя, и при этом выполнить действие от имени другого пользователя со злым умыслом.
К сожалению, согласно RFC 8259 существует множество текстов JSON, которые синтаксически допустимы, но семантически неоднозначны. Например, стандарт не определяет способ интерпретации повторяющихся имен внутри объекта.
Пакеты v1 encoding/json и encoding/json/v2 по-разному интерпретируют некоторые входные данные. В частности:
Стандарт требует, чтобы JSON кодировался с использованием UTF-8. По умолчанию v1 заменяет недопустимые байты UTF-8 в строках JSON символом замены Unicode, а v2 отклоняет входные данные с некорректной UTF-8. Чтобы изменить значение по умолчанию, укажите параметр jsontext.AllowInvalidUTF8. Замена некорректной UTF-8 — это форма повреждения данных, изменяющая точный смысл строк.
Стандарт не задает конкретное поведение при обнаружении повторяющихся имен в объекте JSON, а значит, разные реализации могут вести себя по-разному. По умолчанию v1 допускает повторяющиеся имена, а v2 отклоняет их. Чтобы изменить значение по умолчанию, укажите параметр jsontext.AllowDuplicateNames. Если повторяющиеся имена разрешены, элементы объекта обрабатываются в порядке обнаружения: более поздние значения заменяют предыдущие или объединяются с ними в зависимости от типа значения Go.
Стандарт определяет объект JSON как неупорядоченную коллекцию пар имя/значение. Хотя порядок можно наблюдать через нижележащий API jsontext, и v1, и v2 обычно не предоставляют доступ к этому порядку. Ни одно приложение не должно семантически зависеть от порядка элементов объекта. Разрешение повторяющихся имен создает возможность случайно обнаружить порядок элементов и начать от него зависеть.
Стандарт рекомендует обычно сравнивать имена объектов JSON на равенство последовательностей кодовых точек Unicode, что означает, что сравнение имен часто чувствительно к регистру. При демаршалинге объекта JSON в структуру Go по умолчанию v1 использует (нестрогое) сопоставление имен без учета регистра, а v2 — (строгое) сопоставление с учетом регистра. Чтобы изменить значение по умолчанию, укажите параметр MatchCaseInsensitiveNames. Сопоставление без учета регистра создает еще одну возможность появления повторяющихся имен. Разрешение сопоставления без учета регистра означает, что v1 или v2 могут интерпретировать объекты JSON иначе, чем большинство других реализаций JSON (обычно использующих сопоставление с учетом регистра).
Стандарт не задает конкретное поведение при обнаружении неизвестного имени в объекте JSON. При демаршалинге объекта JSON в структуру Go и v1, и v2 по умолчанию игнорируют неизвестные имена и соответствующие им значения. Чтобы изменить значение по умолчанию, укажите параметр RejectUnknownMembers.
Стандарт допускает, что реализации могут использовать float64 для представления числа JSON. Поэтому большие целые числа JSON могут терять точность при сохранении в типе с плавающей точкой. При маршалинге и демаршалинге конкретного целочисленного типа и v1, и v2 корректно сохраняют точность. Однако, даже если v1 и v2 сохраняют точность для конкретных типов, другие реализации JSON могут не сохранить точность результатов, созданных v1 или v2. Параметр тега `string` можно использовать, чтобы указать, что целочисленный тип следует заключать в строку JSON, предотвращая потерю точности. Кроме того, v1 и v2 все еще могут терять точность при демаршалинге в значение интерфейса any, поскольку по умолчанию для представления числа JSON при демаршалинге используется float64. Чтобы изменить значение по умолчанию, укажите параметр WithUnmarshalers с пользовательским демаршалером, который предварительно задает для значения интерфейса конкретный тип Go, способный сохранять точность.
RFC 8785 определяет каноническую форму любого текста JSON и явно задает конкретное поведение, которое RFC 8259 оставляет неопределенным. Теоретически, если текст можно успешно обработать с помощью jsontext.Value.Canonicalize без изменения семантического смысла данных, это дает больше уверенности в безопасности и совместимости данных.
API v2 обычно выбирает более безопасные значения по умолчанию, чем v1, но при работе с большими целыми числами или неизвестными элементами все равно следует соблюдать осторожность.
Пример (Учет регистра)
Unmarshal сопоставляет имена объектов JSON с полями структуры Go с учетом регистра, но его можно настроить на сопоставление без учета регистра с помощью параметра "case:ignore". Это позволяет выполнять демаршалинг входных данных, в которых используются такие соглашения об именовании, как camelCase, snake_case или kebab-case.
Код:
// JSON input using various naming conventions.
const input = `[
{"firstname": true},
{"firstName": true},
{"FirstName": true},
{"FIRSTNAME": true},
{"first_name": true},
{"FIRST_NAME": true},
{"first-name": true},
{"FIRST-NAME": true},
{"unknown": true}
]`
// Without "case:ignore", Unmarshal looks for an exact match.
var caseStrict []struct {
X bool `json:"firstName"`
}
if err := json.Unmarshal([]byte(input), &caseStrict); err != nil {
log.Fatal(err)
}
fmt.Println(caseStrict) // exactly 1 match found
// With "case:ignore", Unmarshal looks first for an exact match,
// then for a case-insensitive match if none found.
var caseIgnore []struct {
X bool `json:"firstName,case:ignore"`
}
if err := json.Unmarshal([]byte(input), &caseIgnore); err != nil {
log.Fatal(err)
}
fmt.Println(caseIgnore) // 8 matches found
Результат:
[{false} {true} {false} {false} {false} {false} {false} {false} {false}]
[{true} {true} {true} {true} {true} {true} {true} {true} {false}]
Пример (Встроенные поля)
Объекты JSON можно встраивать в родительский объект, подобно тому как структуры Go можно встраивать в родительскую структуру. Правила встраивания JSON похожи на правила встраивания Go, но применяются к пространству имен JSON.
Код:
// Base is embedded within Container.
type Base struct {
// ID is promoted into the JSON object for Container.
ID string
// Type is ignored due to presence of Container.Type.
Type string
// Time cancels out with Container.Embed.Time.
Time time.Time
}
// Other is embedded within Container.
type Other struct{ Cost float64 }
// Container embeds Base and Other.
type Container struct {
// Base is an embedded struct and is implicitly JSON embedded.
Base
// Type takes precedence over Base.Type.
Type int
// Embed is a named Go field, but is explicitly JSON embedded.
Embed struct {
// User is promoted into the JSON object for Container.
User string
// Time cancels out with Base.Time.
Time string
} `json:",embed"`
// ID does not conflict with Base.ID since the JSON name is different.
ID string `json:"uuid"`
// Other is not JSON embedded since it has an explicit JSON name.
Other `json:"other"`
}
// Format an empty Container to show what fields are JSON serializable.
var input Container
b, err := json.Marshal(&input)
if err != nil {
log.Fatal(err)
}
// Indent output for readability.
v := jsontext.Value(b)
v.Indent()
fmt.Println(string(v))
Результат:
{
"ID": "",
"Type": 0,
"User": "",
"uuid": "",
"other": {
"Cost": 0
}
}
Пример (Имена полей)
По умолчанию имена объектов JSON для полей структур Go формируются на основе имен полей Go, но их можно указать в теге `json`. Благодаря происхождению JSON из JavaScript наиболее распространенным соглашением об именовании объектов JSON является camelCase.
Код:
var value struct {
// This field is explicitly ignored with the special "-" name.
Ignored any `json:"-"`
// No JSON name is not provided, so the Go field name is used.
GoName any
// A JSON name is provided without any special characters.
JSONName any `json:"jsonName"`
// No JSON name is not provided, so the Go field name is used.
Option any `json:",case:ignore"`
// An unexported field is always ignored.
unexported any
}
b, err := json.Marshal(value)
if err != nil {
log.Fatal(err)
}
// Indent output for readability.
v := jsontext.Value(b)
v.Indent()
fmt.Println(string(v))
Результат:
{
"GoName": null,
"jsonName": null,
"Option": null
}
Пример (Пропуск полей)
Поля структур Go можно пропускать в зависимости от входного значения Go или результата кодирования этого значения в JSON. Параметр "omitzero" пропускает поле, если оно является нулевым значением Go или реализует метод "IsZero() bool", возвращающий true. Параметр "omitempty" пропускает поле, если оно кодируется как пустое значение JSON; под ним понимаются null или пустая строка, объект либо массив JSON. Во многих случаях параметры "omitzero" и "omitempty" работают одинаково. Если желаемого результата можно добиться с помощью обоих параметров, предпочтительно использовать "omitzero".
Код:
type MyStruct struct {
Foo string `json:",omitzero"`
Bar []int `json:",omitempty"`
// Both "omitzero" and "omitempty" can be specified together,
// in which case the field is omitted if either would take effect.
// This omits the Baz field either if it is a nil pointer or
// if it would have encoded as an empty JSON object.
Baz *MyStruct `json:",omitzero,omitempty"`
}
// Demonstrate behavior of "omitzero".
b, err := json.Marshal(struct {
Bool bool `json:",omitzero"`
Int int `json:",omitzero"`
String string `json:",omitzero"`
Time time.Time `json:",omitzero"`
Addr netip.Addr `json:",omitzero"`
Struct MyStruct `json:",omitzero"`
SliceNil []int `json:",omitzero"`
Slice []int `json:",omitzero"`
MapNil map[int]int `json:",omitzero"`
Map map[int]int `json:",omitzero"`
PointerNil *string `json:",omitzero"`
Pointer *string `json:",omitzero"`
InterfaceNil any `json:",omitzero"`
Interface any `json:",omitzero"`
}{
// Bool is omitted since false is the zero value for a Go bool.
Bool: false,
// Int is omitted since 0 is the zero value for a Go int.
Int: 0,
// String is omitted since "" is the zero value for a Go string.
String: "",
// Time is omitted since time.Time.IsZero reports true.
Time: time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC),
// Addr is omitted since netip.Addr{} is the zero value for a Go struct.
Addr: netip.Addr{},
// Struct is NOT omitted since it is not the zero value for a Go struct.
Struct: MyStruct{Bar: []int{}, Baz: new(MyStruct)},
// SliceNil is omitted since nil is the zero value for a Go slice.
SliceNil: nil,
// Slice is NOT omitted since []int{} is not the zero value for a Go slice.
Slice: []int{},
// MapNil is omitted since nil is the zero value for a Go map.
MapNil: nil,
// Map is NOT omitted since map[int]int{} is not the zero value for a Go map.
Map: map[int]int{},
// PointerNil is omitted since nil is the zero value for a Go pointer.
PointerNil: nil,
// Pointer is NOT omitted since new(string) is not the zero value for a Go pointer.
Pointer: new(string),
// InterfaceNil is omitted since nil is the zero value for a Go interface.
InterfaceNil: nil,
// Interface is NOT omitted since (*string)(nil) is not the zero value for a Go interface.
Interface: (*string)(nil),
})
if err != nil {
log.Fatal(err)
}
// Indent output for readability.
v := jsontext.Value(b)
v.Indent()
fmt.Println("OmitZero:", string(v)) // outputs "Struct", "Slice", "Map", "Pointer", and "Interface"
// Demonstrate behavior of "omitempty".
b, err = json.Marshal(struct {
Bool bool `json:",omitempty"`
Int int `json:",omitempty"`
String string `json:",omitempty"`
Time time.Time `json:",omitempty"`
Addr netip.Addr `json:",omitempty"`
Struct MyStruct `json:",omitempty"`
Slice []int `json:",omitempty"`
Map map[int]int `json:",omitempty"`
PointerNil *string `json:",omitempty"`
Pointer *string `json:",omitempty"`
InterfaceNil any `json:",omitempty"`
Interface any `json:",omitempty"`
}{
// Bool is NOT omitted since false is not an empty JSON value.
Bool: false,
// Int is NOT omitted since 0 is not a empty JSON value.
Int: 0,
// String is omitted since "" is an empty JSON string.
String: "",
// Time is NOT omitted since this encodes as a non-empty JSON string.
Time: time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC),
// Addr is omitted since this encodes as an empty JSON string.
Addr: netip.Addr{},
// Struct is omitted since {} is an empty JSON object.
Struct: MyStruct{Bar: []int{}, Baz: new(MyStruct)},
// Slice is omitted since [] is an empty JSON array.
Slice: []int{},
// Map is omitted since {} is an empty JSON object.
Map: map[int]int{},
// PointerNil is omitted since null is an empty JSON value.
PointerNil: nil,
// Pointer is omitted since "" is an empty JSON string.
Pointer: new(string),
// InterfaceNil is omitted since null is an empty JSON value.
InterfaceNil: nil,
// Interface is omitted since null is an empty JSON value.
Interface: (*string)(nil),
})
if err != nil {
log.Fatal(err)
}
// Indent output for readability.
v = jsontext.Value(b)
v.Indent()
fmt.Println("OmitEmpty:", string(v)) // outputs "Bool", "Int", and "Time"
Результат:
OmitZero: {
"Struct": {},
"Slice": [],
"Map": {},
"Pointer": "",
"Interface": null
}
OmitEmpty: {
"Bool": false,
"Int": 0,
"Time": "0001-01-01T00:00:00Z"
}
Пример (Упорядоченный объект)
Точный порядок элементов объекта JSON можно сохранить с помощью специального типа, реализующего [MarshalerTo] и [UnmarshalerFrom].
Код:
package json_test
import (
"fmt"
"log"
"reflect"
"encoding/json/jsontext"
"encoding/json/v2"
)
// OrderedObject is an ordered sequence of name/value members in a JSON object.
//
// RFC 8259 defines an object as an "unordered collection".
// JSON implementations need not make "ordering of object members visible"
// to applications nor will they agree on the semantic meaning of an object if
// "the names within an object are not unique". For maximum compatibility,
// applications should avoid relying on ordering or duplicity of object names.
type OrderedObject[V any] []ObjectMember[V]
// ObjectMember is a JSON object member.
type ObjectMember[V any] struct {
Name string
Value V
}
// MarshalJSONTo encodes obj as a JSON object into enc.
func (obj *OrderedObject[V]) MarshalJSONTo(enc *jsontext.Encoder) error {
if err := enc.WriteToken(jsontext.BeginObject); err != nil {
return err
}
for i := range *obj {
member := &(*obj)[i]
if err := json.MarshalEncode(enc, &member.Name); err != nil {
return err
}
if err := json.MarshalEncode(enc, &member.Value); err != nil {
return err
}
}
if err := enc.WriteToken(jsontext.EndObject); err != nil {
return err
}
return nil
}
// UnmarshalJSONFrom decodes a JSON object from dec into obj.
func (obj *OrderedObject[V]) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if k := dec.PeekKind(); k != '{' {
// The [json] package automatically populates relevant fields
// in a [json.SemanticError] to provide additional context.
return &json.SemanticError{JSONKind: k}
}
if _, err := dec.ReadToken(); err != nil {
return err
}
for dec.PeekKind() != '}' {
*obj = append(*obj, ObjectMember[V]{})
member := &(*obj)[len(*obj)-1]
if err := json.UnmarshalDecode(dec, &member.Name); err != nil {
return err
}
if err := json.UnmarshalDecode(dec, &member.Value); err != nil {
return err
}
}
if _, err := dec.ReadToken(); err != nil {
return err
}
return nil
}
// The exact order of JSON object can be preserved through the use of a
// specialized type that implements [MarshalerTo] and [UnmarshalerFrom].
func Example_orderedObject() {
// Round-trip marshal and unmarshal an ordered object.
// We expect the order and duplicity of JSON object members to be preserved.
// Specify jsontext.AllowDuplicateNames since this object contains "fizz" twice.
want := OrderedObject[string]{
{"fizz", "buzz"},
{"hello", "world"},
{"fizz", "wuzz"},
}
b, err := json.Marshal(&want, jsontext.AllowDuplicateNames(true))
if err != nil {
log.Fatal(err)
}
var got OrderedObject[string]
err = json.Unmarshal(b, &got, jsontext.AllowDuplicateNames(true))
if err != nil {
log.Fatal(err)
}
// Sanity check.
if !reflect.DeepEqual(got, want) {
log.Fatalf("roundtrip mismatch: got %v, want %v", got, want)
}
// Indent output for readability.
v := jsontext.Value(b)
v.Indent()
fmt.Println(string(v))
// Output:
// {
// "fizz": "buzz",
// "hello": "world",
// "fizz": "wuzz"
// }
}
Пример (ProtoJSON)
Для некоторых типов Go используется пользовательское представление в JSON, реализация которого делегируется внешнему пакету. Поэтому пакет "json" не знает, как использовать эту внешнюю реализацию. Например, пакет [google.golang.org/protobuf/encoding/protojson] реализует JSON для всех типов [google.golang.org/protobuf/proto.Message]. Параметры [WithMarshalers] и [WithUnmarshalers] позволяют настроить совместную работу "json" и "protojson".
Код:
// Let protoMessage be "google.golang.org/protobuf/proto".Message.
type protoMessage interface{ ProtoReflect() }
// Let foopbMyMessage be a concrete implementation of proto.Message.
type foopbMyMessage struct{ protoMessage }
// Let protojson be an import of "google.golang.org/protobuf/encoding/protojson".
var protojson struct {
Marshal func(protoMessage) ([]byte, error)
Unmarshal func([]byte, protoMessage) error
}
// This value mixes both non-proto.Message types and proto.Message types.
// It should use the "json" package to handle non-proto.Message types and
// should use the "protojson" package to handle proto.Message types.
var value struct {
// GoStruct does not implement proto.Message and
// should use the default behavior of the "json" package.
GoStruct struct {
Name string
Age int
}
// ProtoMessage implements proto.Message and
// should be handled using protojson.Marshal.
ProtoMessage *foopbMyMessage
}
// Marshal using protojson.Marshal for proto.Message types.
b, err := json.Marshal(&value,
// Use protojson.Marshal as a type-specific marshaler.
json.WithMarshalers(json.MarshalFunc(protojson.Marshal)))
if err != nil {
log.Fatal(err)
}
// Unmarshal using protojson.Unmarshal for proto.Message types.
err = json.Unmarshal(b, &value,
// Use protojson.Unmarshal as a type-specific unmarshaler.
json.WithUnmarshalers(json.UnmarshalFunc(protojson.Unmarshal)))
if err != nil {
log.Fatal(err)
}
Пример (Обработка HTTP-запросов)
При реализации конечных точек HTTP часто приходится работать с [io.Reader] и [io.Writer]. Функции [MarshalWrite] и [UnmarshalRead] помогают работать с такими типами ввода-вывода. [UnmarshalRead] считывает все содержимое [io.Reader], чтобы убедиться, что достигнут [io.EOF] и после значения JSON верхнего уровня нет неожиданных байтов.
Код:
// Some global state maintained by the server.
var n int64
// The "add" endpoint accepts a POST request with a JSON object
// containing a number to atomically add to the server's global counter.
// It returns the updated value of the counter.
http.HandleFunc("/api/add", func(w http.ResponseWriter, r *http.Request) {
// Unmarshal the request from the client.
var val struct{ N int64 }
if err := json.UnmarshalRead(r.Body, &val); err != nil {
// Inability to unmarshal the input suggests a client-side problem.
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// Marshal a response from the server.
val.N = atomic.AddInt64(&n, val.N)
if err := json.MarshalWrite(w, &val); err != nil {
// Inability to marshal the output suggests a server-side problem.
// This error is not always observable by the client since
// json.MarshalWrite may have already written to the output.
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
})
Пример (Маршалинг текста)
Если тип реализует [encoding.TextMarshaler] и/или [encoding.TextUnmarshaler], то методы MarshalText и UnmarshalText используются для кодирования или декодирования значения в строку JSON или из нее.
Код:
// Round-trip marshal and unmarshal a hostname map where the netip.Addr type
// implements both encoding.TextMarshaler and encoding.TextUnmarshaler.
want := map[netip.Addr]string{
netip.MustParseAddr("192.168.0.100"): "carbonite",
netip.MustParseAddr("192.168.0.101"): "obsidian",
netip.MustParseAddr("192.168.0.102"): "diamond",
}
b, err := json.Marshal(&want, json.Deterministic(true))
if err != nil {
log.Fatal(err)
}
var got map[netip.Addr]string
err = json.Unmarshal(b, &got)
if err != nil {
log.Fatal(err)
}
// Sanity check.
if !reflect.DeepEqual(got, want) {
log.Fatalf("roundtrip mismatch: got %v, want %v", got, want)
}
// Indent output for readability.
v := jsontext.Value(b)
v.Indent()
fmt.Println(string(v))
Результат:
{
"192.168.0.100": "carbonite",
"192.168.0.101": "obsidian",
"192.168.0.102": "diamond"
}
Указатель
Примеры
- Marshal (Многострочный вывод)
- MarshalerTo
- UnmarshalDecode (Поток)
- UnmarshalerFrom
- WithMarshalers (Ошибки)
- WithUnmarshalers (Сырое число)
- WithUnmarshalers (Смещения записей)
- Пакет (Учет регистра)
- Пакет (Встроенные поля)
- Пакет (Имена полей)
- Пакет (Пропуск полей)
- Пакет (Упорядоченный объект)
- Пакет (ProtoJSON)
- Пакет (Обработка HTTP-запросов)
- Пакет (Маршалинг текста)
Файлы пакета
arshal.go arshal_any.go arshal_default.go arshal_embedded.go arshal_funcs.go arshal_methods.go arshal_time.go doc.go errors.go fields.go fold.go intern.go options.go
Переменные
ErrUnknownName указывает на то, что элемент объекта JSON не удалось демаршализовать, поскольку его имя неизвестно целевой структуре Go. Эта ошибка непосредственно оборачивается в SemanticError, когда возникает.
Имя неизвестного элемента объекта JSON можно извлечь следующим образом:
err := ...
serr, ok := errors.AsType[*json.SemanticError](err)
if ok && serr.Err == json.ErrUnknownName {
ptr := serr.JSONPointer // JSON pointer to unknown name
name := ptr.LastToken() // unknown name itself
...
}
Эта ошибка возвращается только в том случае, если RejectUnknownMembers имеет значение true.
var ErrUnknownName = errors.New("unknown object member name") func GetOption 1.27
func GetOption[T any](opts Options, setter func(T) Options) (T, bool)
GetOption возвращает значение, хранящееся в opts и заданное указанной функцией setter, а также сообщает, присутствует ли это значение. Если значение отсутствует, возвращается нулевое значение типа T.
Пример использования:
v, ok := json.GetOption(opts, json.Deterministic)
Чаще всего параметры анализируют, чтобы изменить представление JSON в методах MarshalerTo.MarshalJSONTo и UnmarshalerFrom.UnmarshalJSONFrom, а также в функциях MarshalToFunc и UnmarshalFromFunc. В таких случаях обычно следует игнорировать бит присутствия.
func Marshal 1.27
func Marshal(in any, opts ...Options) (out []byte, err error)
Marshal сериализует значение Go в []byte согласно предоставленным параметрам маршалинга и кодирования (игнорируя параметры демаршалинга и декодирования). В конец результата не добавляется символ новой строки.
Функции и методы маршалинга для конкретных типов имеют приоритет над представлением значения по умолчанию. Функции или методы, работающие с *T, вызываются только при кодировании значения типа T (путем взятия его адреса) или ненулевого значения *T. Marshal гарантирует, что значение всегда адресуемо (при необходимости копируя его), чтобы эти функции и методы могли вызываться единообразно. Для повышения производительности рекомендуется передавать в Marshal ненулевой указатель на значение.
Если значение типа соответствует каким-либо функциям для конкретных типов в параметре WithMarshalers, эти функции вызываются для кодирования значения. Если все подходящие функции возвращают errors.ErrUnsupported, значение кодируется согласно последующим правилам.
Если тип значения реализует MarshalerTo, для кодирования значения вызывается метод MarshalJSONTo. Если метод возвращает errors.ErrUnsupported, входное значение кодируется согласно последующим правилам.
Если тип значения реализует Marshaler, для кодирования значения вызывается метод MarshalJSON.
Если тип значения реализует encoding.TextAppender, для кодирования значения вызывается метод AppendText, после чего результат кодируется как строка JSON.
Если тип значения реализует encoding.TextMarshaler, для кодирования значения вызывается метод MarshalText, после чего результат кодируется как строка JSON.
В противном случае значение кодируется в соответствии со своим типом, как подробно описано ниже.
Для большинства типов Go по умолчанию используются следующие представления в JSON:
Логическое значение Go кодируется как логическое значение JSON (например, true или false).
Строка Go кодируется как строка JSON.
[]byte или [N]byte в Go кодируется как строка JSON, содержащая двоичное значение в кодировке Base64 согласно разделу 4 RFC 4648.
Целое число Go кодируется как число JSON без дробной части и экспоненты. Если указан параметр StringifyNumbers или кодируется имя объекта JSON, число JSON заключается в строку JSON.
Число с плавающей точкой Go кодируется как число JSON. Если указан параметр StringifyNumbers или кодируется имя объекта JSON, число JSON заключается в строку JSON. Кодирование NaN или ±Inf приводит к ошибке SemanticError.
Карта Go кодируется как объект JSON, где каждый ключ и значение карты Go рекурсивно кодируются как пара имени и значения в объекте JSON. Ключ карты Go должен кодироваться как строка JSON, иначе возникает ошибка SemanticError. Обход карты Go выполняется в недетерминированном порядке. Для детерминированного кодирования можно использовать параметр Deterministic. По умолчанию nil-карта кодируется как пустой объект JSON, если не указан параметр FormatNilMapAsNull.
Структура Go кодируется как объект JSON. Дополнительные сведения см. в разделе «Представление структур Go в JSON» документации пакета.
Срез Go кодируется как массив JSON, в котором каждый элемент среза Go рекурсивно кодируется как элемент массива JSON. По умолчанию nil-срез кодируется как пустой массив JSON, если не указан параметр FormatNilSliceAsNull.
Массив Go кодируется как массив JSON, в котором каждый элемент массива Go рекурсивно кодируется как элемент массива JSON. Длина массива JSON всегда совпадает с длиной массива Go.
Указатель Go кодируется как null JSON, если он равен nil; в противном случае используется рекурсивно закодированное в JSON представление базового значения.
Интерфейс Go кодируется как null JSON, если он равен nil; в противном случае используется рекурсивно закодированное в JSON представление базового значения.
Значение time.Time в Go кодируется как строка JSON, содержащая временную метку в формате RFC 3339 с наносекундной точностью.
Для time.Duration в Go в настоящее время нет представления по умолчанию, поэтому возникает ошибка SemanticError, если не указан параметр encoding/json.FormatDurationAsNano; в этом случае значение кодируется как число JSON без дробной части и экспоненты, представляющее длительность в наносекундах.
Для всех остальных типов Go (например, комплексных чисел, каналов и функций) представление по умолчанию отсутствует, поэтому возникает ошибка SemanticError.
JSON не поддерживает циклические структуры данных, и Marshal не обрабатывает их.
Пример (Многострочный вывод)
Используйте [jsontext.Multiline], чтобы создавать многострочный вывод с отступами, более удобный для чтения человеком. Дополнительные параметры для настройки многострочного вывода см. в [jsontext.Multiline].
Код:
type Pet struct {
Name string
Species string
Breed string
}
p := Pet{
Name: "Oliver",
Species: "Dog",
Breed: "Goldendoodle",
}
b, err := json.Marshal(p, jsontext.Multiline(true))
if err != nil {
log.Fatal(err)
}
fmt.Println(string(b))
Результат:
{
"Name": "Oliver",
"Species": "Dog",
"Breed": "Goldendoodle"
}
func MarshalEncode 1.27
func MarshalEncode(out *jsontext.Encoder, in any, opts ...Options) (err error)
MarshalEncode сериализует значение Go в jsontext.Encoder согласно предоставленным параметрам маршалинга или кодирования (игнорируя параметры демаршалинга или декодирования). Переданные параметры имеют приоритет над параметрами, уже примененными к jsontext.Encoder, и действуют только во время вызова маршалинга.
Подробные сведения о преобразовании значения Go в JSON см. в разделе Marshal.
func MarshalWrite 1.27
func MarshalWrite(out io.Writer, in any, opts ...Options) (err error)
MarshalWrite сериализует значение Go в io.Writer согласно предоставленным параметрам маршалинга и кодирования (игнорируя параметры демаршалинга или декодирования). В конец результата не добавляется символ новой строки. Подробные сведения о преобразовании значения Go в JSON см. в разделе Marshal.
func Unmarshal 1.27
func Unmarshal(in []byte, out any, opts ...Options) (err error)
Unmarshal декодирует входные данные []byte в значение Go согласно предоставленным параметрам демаршалинга и декодирования (игнорируя параметры маршалинга и кодирования). Входные данные должны содержать одно значение JSON с необязательными пробельными символами между его частями. Выходное значение должно быть ненулевым указателем.
Функции и методы демаршалинга для конкретных типов имеют приоритет над представлением значения по умолчанию. Функции или методы, работающие с *T, вызываются только при декодировании значения типа T (путем взятия его адреса) или ненулевого значения *T. Unmarshal гарантирует, что значение всегда адресуемо (при необходимости копируя его), чтобы эти функции и методы могли вызываться единообразно. Если для вызова метода Unmarshaler, UnmarshalerFrom или encoding.TextUnmarshaler с указательным получателем требуется поверхностная копия значения, любые изменения, выполненные методом, поверхностно копируются обратно в целевое значение.
Входные данные декодируются в выходное значение согласно следующим правилам:
Если какая-либо типоспецифичная функция в параметре WithUnmarshalers соответствует типу значения, эти функции вызываются для декодирования значения JSON. Если все подходящие функции возвращают errors.ErrUnsupported, входные данные декодируются согласно последующим правилам.
Если тип значения реализует UnmarshalerFrom, для декодирования значения JSON вызывается метод UnmarshalJSONFrom. Если метод возвращает errors.ErrUnsupported, входные данные декодируются согласно последующим правилам.
Если тип значения реализует Unmarshaler, для декодирования значения JSON вызывается метод UnmarshalJSON.
Если тип значения реализует encoding.TextUnmarshaler, входные данные декодируются как строка JSON, а метод UnmarshalText вызывается с декодированным строковым значением. Если входные данные не являются строкой JSON, возникает ошибка SemanticError.
В противном случае значение JSON декодируется в соответствии с типом значения, как подробно описано ниже.
Для большинства типов Go предусмотрено представление JSON по умолчанию. Значение null в JSON можно декодировать в любое поддерживаемое значение Go, если оно эквивалентно сохранению нулевого значения этого значения Go. Если вид входного значения JSON не обрабатывается текущим типом значения Go, возникает ошибка SemanticError. Если не указано иное, декодированное значение заменяет любое ранее существовавшее значение.
Представление каждого типа выглядит следующим образом:
Логическое значение Go декодируется из логического значения JSON (например, true или false).
Строка Go декодируется из строки JSON.
Go []byte или [N]byte декодируется из строки JSON, содержащей двоичное значение, закодированное в Base64 согласно разделу 4 RFC 4648. При декодировании в ненулевой []byte длина среза сбрасывается до нуля, а декодированные входные данные добавляются в него. При декодировании в [N]byte входные данные должны декодироваться ровно в N байт, иначе возникает ошибка SemanticError.
Целое число Go декодируется из числа JSON. Если указан StringifyNumbers или декодируется имя объекта JSON, оно должно декодироваться из строки JSON, содержащей число JSON. Возникает ошибка SemanticError, если число JSON содержит дробную часть или экспоненту. Ошибка также возникает, если число выходит за пределы представления целочисленного типа Go.
Число с плавающей точкой Go декодируется из числа JSON. Если указан StringifyNumbers или декодируется имя объекта JSON, оно должно декодироваться из строки JSON, содержащей число JSON. Возникает ошибка, если число выходит за пределы представления типа Go с плавающей точкой. Поскольку в JSON нет встроенного представления для NaN или ±Inf, декодирование не может привести к таким значениям.
Карта Go декодируется из объекта JSON, в котором каждая пара имени и значения объекта JSON рекурсивно декодируется как ключ и значение карты Go. Карты не очищаются. Если карта Go равна nil, для декодирования выделяется новая карта. Если декодированный ключ совпадает с существующей записью карты Go, значение этой записи повторно используется: в него декодируется значение объекта JSON.
Структура Go декодируется из объекта JSON. Дополнительные сведения см. в разделе «Представление структур Go в JSON» документации на уровне пакета.
Срез Go декодируется из массива JSON: каждый элемент JSON рекурсивно декодируется и добавляется в срез Go. Перед добавлением в срез Go выделяется новый срез, если текущий равен nil; в противном случае длина среза сбрасывается до нуля.
Массив Go декодируется из массива JSON: каждый элемент массива JSON рекурсивно декодируется в соответствующий элемент массива Go. Перед декодированием каждый элемент массива Go обнуляется. Если массив JSON содержит не ровно столько же элементов, сколько массив Go, возникает ошибка SemanticError.
Указатель Go декодируется на основе вида JSON и базового типа Go. Если входное значение — null в JSON, указателю присваивается nil. В противном случае, если указатель равен nil, выделяется новое базовое значение, и в него рекурсивно декодируется JSON.
Интерфейс Go декодируется на основе вида JSON и базового типа Go. Если входное значение — null в JSON, интерфейсу присваивается nil. В противном случае интерфейс со значением nil и пустым типом интерфейса инициализируется нулевым значением Go типа bool, string, float64, map[string]any или []any, если входное значение является логическим значением, строкой, числом, объектом или массивом JSON соответственно. Если значение интерфейса по-прежнему равно nil, возникает ошибка SemanticError, поскольку не удалось определить подходящий тип Go для декодирования. Например, декодирование в nil io.Reader завершается ошибкой, так как нет конкретного типа, которым можно было бы заполнить значение интерфейса. В противном случае базовое значение существует, и входные данные JSON рекурсивно декодируются в него.
Go time.Time декодируется из строки JSON, содержащей время в формате RFC 3339 с точностью до наносекунд.
Для Go time.Duration в настоящее время не предусмотрено представление по умолчанию, поэтому возникает ошибка SemanticError, если не указан параметр encoding/json.FormatDurationAsNano. В этом случае значение декодируется как число JSON без дробной части и экспоненты, представляющее длительность в наносекундах.
Для всех остальных типов Go (например, комплексных чисел, каналов и функций) не предусмотрено представление по умолчанию, поэтому возникает ошибка SemanticError.
В целом декодирование следует семантике слияния (аналогичной RFC 7396): декодированное значение Go заменяет значение назначения для любого вида JSON, кроме объекта. Для объектов JSON входной объект сливается со значением назначения, при этом к совпадающим членам объекта рекурсивно применяется семантика слияния.
func UnmarshalDecode 1.27
func UnmarshalDecode(in *jsontext.Decoder, out any, opts ...Options) (err error)
UnmarshalDecode десериализует значение Go из jsontext.Decoder согласно предоставленным параметрам unmarshal или decode (игнорируя параметры marshal или encode). Предоставленные параметры имеют приоритет над параметрами, уже примененными к jsontext.Decoder, и действуют только во время вызова unmarshal.
Входные данные могут представлять собой поток из нуля или более значений JSON. UnmarshalDecode декодирует только следующее значение JSON в потоке. Если значений JSON верхнего уровня больше нет, функция возвращает io.EOF. Результат должен быть ненулевым указателем. Сведения о преобразовании JSON в значение Go см. в Unmarshal.
Пример (поток)
UnmarshalDecode можно использовать для декодирования потока значений JSON, разделенных пробельными символами.
Код:
const jsonStream = `
{"Name": "Platypus", "Order": "Monotremata"}
{"Name": "Quoll", "Order": "Dasyuromorphia"}
{"Name": "Gopher", "Order": "Rodentia"}
`
type Animal struct {
Name string
Order string
}
dec := jsontext.NewDecoder(strings.NewReader(jsonStream))
for {
var a Animal
if err := json.UnmarshalDecode(dec, &a); err == io.EOF {
break
} else if err != nil {
log.Fatal(err)
}
fmt.Printf("%s: %s\n", a.Name, a.Order)
}
Результат:
Platypus: Monotremata Quoll: Dasyuromorphia Gopher: Rodentia
func UnmarshalRead 1.27
func UnmarshalRead(in io.Reader, out any, opts ...Options) (err error)
UnmarshalRead десериализует значение Go из io.Reader согласно предоставленным параметрам unmarshal и decode (игнорируя параметры marshal или encode). Входные данные должны содержать одно значение JSON с возможными пробельными символами между его частями. Функция считывает всё содержимое io.Reader до получения io.EOF, не возвращая ошибку при достижении конца файла. Результат должен быть ненулевым указателем. Сведения о преобразовании JSON в значение Go см. в Unmarshal.
type Marshaler 1.27
Marshaler реализуется типами, способными выполнять маршалинг самостоятельно. Рекомендуется реализовывать MarshalerTo, если только реализация не пытается избежать прямой зависимости от пакета "jsontext".
Реализации должны возвращать буфер, который безопасно сохранять и, возможно, изменять вызывающему коду.
Реализации не должны возвращать errors.ErrUnsupported.
Если возвращенная ошибка является SemanticError, пакет json может заполнить незаполненные поля ошибки дополнительным контекстом. Ошибки других типов оборачиваются в SemanticError.
Реализации должны исходить из того, что Deterministic имеет значение true, и возвращать детерминированный результат.
type Marshaler interface {
MarshalJSON() ([]byte, error)
} type MarshalerTo 1.27
MarshalerTo реализуется типами, способными выполнять маршалинг самостоятельно. Рекомендуется реализовывать MarshalerTo вместо Marshaler, поскольку этот интерфейс более производителен и гибок. Если тип реализует и Marshaler, и MarshalerTo, приоритет имеет MarshalerTo. В этом случае обе реализации должны стремиться к эквивалентному поведению при параметрах маршалинга по умолчанию.
Реализация должна записывать в Encoder только одно значение JSON. В качестве альтернативы она может вернуть errors.ErrUnsupported, не изменяя Encoder. Пакет "json", вызывающий этот метод, использует следующее доступное представление JSON для типа-получателя, как описано в Marshal. Реализации не должны сохранять указатель на jsontext.Encoder.
Если возвращенная ошибка является SemanticError, пакет json может заполнить незаполненные поля ошибки дополнительным контекстом. Ошибки других типов оборачиваются в SemanticError, за исключением ошибок ввода-вывода.
Не следует вызывать метод MarshalJSONTo напрямую, поскольку он может возвращать sentinel-ошибки, требующие особой обработки. Вместо этого следует вызывать MarshalEncode, который обрабатывает такие случаи.
Реализациям следует проверять параметры маршалинга через jsontext.Encoder.Options и при необходимости изменять поведение с учетом этих параметров.
Для реализаций MarshalerTo могут быть важны следующие параметры:
- Deterministic: если реализация может выдавать недетерминированный результат - StringifyNumbers: если тип представлен как число JSON
Некоторые параметры, например FormatNilSliceAsNull, применяются только к встроенным типам Go. Поэтому обычно они не имеют прямого отношения к реализациям MarshalerTo. Однако типы, представляющие составной тип, должны выполнять маршалинг содержащихся типов с помощью MarshalEncode, чтобы эти параметры применялись к содержащимся типам. Аналогично, WithMarshalers может влиять на маршалинг любого содержащегося типа в составе составного типа.
Все остальные параметры обрабатываются автоматически вне реализации MarshalerTo и потому не имеют отношения к реализациям.
type MarshalerTo interface {
MarshalJSONTo(*jsontext.Encoder) error
} Пример
Для пользовательских типов можно задать собственное поведение маршалинга с помощью [MarshalerTo].
Код:
set := IntSet{
1: {},
2: {},
3: {},
}
b, err := json.Marshal(&set, json.Deterministic(true))
if err != nil {
log.Fatal(err)
}
// Indent output for readability.
v := jsontext.Value(b)
v.Indent()
fmt.Println(string(v))
Результат:
[ 1, 2, 3 ]
type Marshalers 1.27
Marshalers — это список функций, которые могут переопределять поведение маршалинга для определенных типов. Заполните WithMarshalers, чтобы использовать этот список с Marshal, MarshalWrite или MarshalEncode. Nil-указатель *Marshalers эквивалентен пустому списку. У Marshalers нет экспортируемых полей или методов.
type Marshalers = typedMarshalers
func JoinMarshalers 1.27
func JoinMarshalers(ms ...*Marshalers) *Marshalers
JoinMarshalers создает плоский список функций маршалинга. Если для значения заданного типа применимы несколько функций из списка, приоритет имеют те, что расположены раньше. Если функция возвращает errors.ErrUnsupported, вызывается следующая подходящая функция; в противном случае используется поведение маршалинга по умолчанию.
Например:
m1 := JoinMarshalers(f1, f2) m2 := JoinMarshalers(f0, m1, f3) // equivalent to m3 m3 := JoinMarshalers(f0, f1, f2, f3) // equivalent to m2
func MarshalFunc 1.27
func MarshalFunc[T any](fn func(T) ([]byte, error)) *Marshalers
MarshalFunc создает типоспецифичный маршалер, задающий способ маршалинга значений типа T. T может быть любым типом, кроме именованного указателя. Если T является типом интерфейса или указателя, функции всегда передается ненулевое значение указателя.
Реализации должны соответствовать требованиям Marshaler.
Реализации не должны сохранять значение T.
func MarshalToFunc 1.27
func MarshalToFunc[T any](fn func(*jsontext.Encoder, T) error) *Marshalers
MarshalToFunc создает типоспецифичный маршалер, задающий способ маршалинга значений типа T. T может быть любым типом, кроме именованного указателя. Если T является типом интерфейса или указателя, функции всегда передается ненулевое значение указателя.
Реализации должны соответствовать требованиям MarshalerTo.
Реализации не должны сохранять указатель на jsontext.Encoder или значение T.
type Options 1.27
Options настраивает Marshal, MarshalWrite, MarshalEncode, Unmarshal, UnmarshalRead и UnmarshalDecode, включая определенные возможности. Каждая функция принимает переменное число параметров; значения свойств, заданные в более поздних параметрах, переопределяют ранее заданные.
Тип Options идентичен типам encoding/json.Options и encoding/json/jsontext.Options. Параметры из других пакетов можно взаимозаменяемо использовать с функциональностью этого пакета.
Значение Options представляет один параметр или набор параметров. Его можно представить как карту Go со свойствами параметров (хотя в базовой реализации для повышения производительности карты Go не используются).
Конструкторы (например, Deterministic) возвращают значение для одного параметра:
opt := Deterministic(true)
что аналогично созданию карты с одной записью:
opt := Options{"Deterministic": true}
JoinOptions объединяет несколько значений параметров:
out := JoinOptions(opts...)
что аналогично созданию новой карты и копированию в нее параметров:
out := make(Options)
for _, m := range opts {
for k, v := range m {
out[k] = v
}
}
GetOption выполняет поиск значения параметра:
v, ok := GetOption(opts, Deterministic)
что аналогично поиску по карте Go:
v, ok := Options["Deterministic"]
Существует один тип Options, используемый как для маршалинга, так и для анмаршалинга. Некоторые параметры влияют на обе операции, другие — только на одну из них:
- StringifyNumbers влияет на маршалинг и анмаршалинг
- Deterministic влияет только на маршалинг
- FormatNilSliceAsNull влияет только на маршалинг
- FormatNilMapAsNull влияет только на маршалинг
- OmitZeroStructFields влияет только на маршалинг
- MatchCaseInsensitiveNames влияет на маршалинг и анмаршалинг
- RejectUnknownMembers влияет только на анмаршалинг
- WithMarshalers влияет только на маршалинг
- WithUnmarshalers влияет только на анмаршалинг
Параметры, не влияющие на конкретную операцию, игнорируются.
type Options = jsonopts.Options
func DefaultOptionsV2 1.27
func DefaultOptionsV2() Options
DefaultOptionsV2 — это полный набор всех параметров, определяющих семантику версии 2. Он эквивалентен набору параметров из encoding/json.DefaultOptionsV1, для которых заданы значения false. Все остальные параметры отсутствуют.
func Deterministic 1.27
func Deterministic(v bool) Options
Deterministic указывает, что маршалинг одного и того же входного значения всегда будет создавать одинаковые выходные байты.
Например, карты Go маршалятся с сортировкой по ключу.
Для встроенных типов Go детерминированность гарантируется для разных экземпляров идентичных бинарных файлов, но не для разных сборок программы (например, с разными исходным кодом или версией цепочки инструментов, значениями GOOS/GOARCH или флагами сборки).
Тип Go с пользовательским маршалером также должен учитывать параметр Deterministic и выполнять детерминированную сериализацию, если он имеет значение true.
Этот параметр влияет только на маршалинг и игнорируется при анмаршалинге.
func FormatNilMapAsNull 1.27
func FormatNilMapAsNull(v bool) Options
FormatNilMapAsNull указывает, что nil-карта Go должна маршалиться как null в JSON, а не в представлении по умолчанию — пустой объект JSON.
Этот параметр влияет только на маршалинг и игнорируется при анмаршалинге.
func FormatNilSliceAsNull 1.27
func FormatNilSliceAsNull(v bool) Options
FormatNilSliceAsNull указывает, что nil-срез Go должен маршалиться как null в JSON, а не в представлении по умолчанию — пустой массив JSON (или пустая строка JSON в случае ~[]byte).
Этот параметр влияет только на маршалинг и игнорируется при анмаршалинге.
func JoinOptions 1.27
func JoinOptions(srcs ...Options) Options
JoinOptions объединяет предоставленный список параметров в один Options. Значения свойств, заданные в более поздних параметрах, переопределяют ранее заданные.
func MatchCaseInsensitiveNames 1.27
func MatchCaseInsensitiveNames(v bool) Options
MatchCaseInsensitiveNames указывает, что члены объектов JSON сопоставляются с полями структур Go без учета регистра имени. Если имя совпадает с несколькими полями, выбирается поле, имя которого совпадает точно. Если такого поля нет, возвращается ошибка. Для полей структур Go, явно помеченных `case:strict` или `case:ignore`, всегда используется сопоставление имен с учетом или без учета регистра соответственно, независимо от значения этого параметра.
Этот параметр влияет на маршалинг и анмаршалинг.
Сопоставление имен без учета регистра также влияет на обнаружение дублирующихся имен (если jsontext.AllowDuplicateNames имеет значение false), поскольку варианты одного имени могут соответствовать одному полю структуры Go. Например, при анмаршалинге имена "foo" и "Foo" могут соответствовать одному полю структуры Go и поэтому считаться дублирующимися. При маршалинге обычно невозможно, чтобы два поля структуры Go сериализовались так, чтобы при анмаршалинге соответствовать одному и тому же полю структуры Go, поскольку все они имеют уникальные точные имена. Однако встроенный резервный элемент может содержать имя, совпадающее с именем поля структуры Go, что приведет к ошибке дублирующегося имени.
func OmitZeroStructFields 1.27
func OmitZeroStructFields(v bool) Options
OmitZeroStructFields указывает, что поля структуры Go с нулевыми значениями должны исключаться из результата маршалинга. Значение считается нулевым, если его тип имеет метод "IsZero() bool", возвращающий true, или, при отсутствии такого метода, если значение является нулевым значением Go. Этот параметр эквивалентен указанию параметра тега `omitzero` для каждого поля структуры Go.
Этот параметр влияет только на маршалинг и игнорируется при анмаршалинге.
func RejectUnknownMembers 1.27
func RejectUnknownMembers(v bool) Options
RejectUnknownMembers указывает, что неизвестные члены следует отклонять при анмаршалинге объекта JSON.
Этот параметр влияет только на анмаршалинг и игнорируется при маршалинге.
func StringifyNumbers 1.27
func StringifyNumbers(v bool) Options
StringifyNumbers указывает, что типы, которые обычно кодируются как число JSON, должны кодироваться как строка JSON, содержащая эквивалентное значение числа JSON. При анмаршалинге значение разбирается из строки JSON, содержащей число JSON без окружающих пробельных символов.
Указание параметра тега `string` для поля структуры Go применяет этот параметр к значению JSON верхнего уровня для этого поля. При применении через параметр тега `string` параметр StringifyNumbers не распространяется рекурсивно на вложенные числа JSON в объекте или массиве JSON.
Как и все параметры, явное указание этого параметра при вызове Marshal, Unmarshal и т. д. приводит к его рекурсивному применению.
Тип Go с пользовательской реализацией маршалинга/анмаршалинга, представляющий число JSON, должен учитывать параметр StringifyNumbers и при его наличии сериализовать значение как число JSON внутри строки JSON. Пользовательская реализация маршалинга/анмаршалинга должна обрабатывать вложенные объекты JSON с помощью MarshalEncode/UnmarshalDecode, которые автоматически применяют поведение параметра тега `string` без рекурсивного распространения.
Согласно разделу 6 RFC 8259 реализация JSON может ограничить представление числа JSON значением IEEE 754 binary64. Это может привести к потере точности декодерами для типов int64 и uint64. Заключение чисел JSON в строку JSON сохраняет точность без потерь.
Этот параметр влияет на маршалинг и анмаршалинг.
func WithMarshalers 1.27
func WithMarshalers(v *Marshalers) Options
WithMarshalers задает список типоспецифичных маршалеров, которые можно использовать для переопределения поведения маршалинга по умолчанию для значений определенных типов.
Этот параметр влияет только на маршалинг и игнорируется при анмаршалинге.
Пример (ошибки)
Многие типы ошибок нельзя сериализовать, поскольку обычно это структуры Go без экспортируемых полей (например, ошибки, созданные с помощью [errors.New]). В некоторых приложениях может потребоваться маршалить ошибку как строку JSON, даже если эти ошибки нельзя анмаршалить.
Код:
// Response to serialize with some Go errors encountered.
response := []struct {
Result string `json:",omitzero"`
Error error `json:",omitzero"`
}{
{Result: "Oranges are a good source of Vitamin C."},
{Error: &strconv.NumError{Func: "ParseUint", Num: "-1234", Err: strconv.ErrSyntax}},
{Error: &os.PathError{Op: "ReadFile", Path: "/path/to/secret/file", Err: os.ErrPermission}},
}
b, err := json.Marshal(&response,
// Intercept every attempt to marshal an error type.
json.WithMarshalers(json.JoinMarshalers(
// Suppose we consider strconv.NumError to be a safe to serialize:
// this type-specific marshal function intercepts this type
// and encodes the error message as a JSON string.
json.MarshalToFunc(func(enc *jsontext.Encoder, err *strconv.NumError) error {
return enc.WriteToken(jsontext.String(err.Error()))
}),
// Error messages may contain sensitive information that may not
// be appropriate to serialize. For all errors not handled above,
// report some generic error message.
json.MarshalFunc(func(error) ([]byte, error) {
return []byte(`"internal server error"`), nil
}),
)),
jsontext.Multiline(true)) // expand for readability
if err != nil {
log.Fatal(err)
}
fmt.Println(string(b))
Результат:
[
{
"Result": "Oranges are a good source of Vitamin C."
},
{
"Error": "strconv.ParseUint: parsing \"-1234\": invalid syntax"
},
{
"Error": "internal server error"
}
]
func WithUnmarshalers 1.27
func WithUnmarshalers(v *Unmarshalers) Options
WithUnmarshalers задает список типоспецифичных анмаршалеров, которые можно использовать для переопределения поведения анмаршалинга по умолчанию для значений определенных типов.
Этот параметр влияет только на анмаршалинг и игнорируется при маршалинге.
Пример (RawNumber)
В некоторых приложениях при анмаршалинге необходимо сохранять точность чисел JSON. Этого можно добиться с помощью типоспецифичной функции анмаршалинга, которая перехватывает все значения типа any и предварительно заполняет значение интерфейса типом [jsontext.Value], способным точно представить число JSON.
Код:
// Input with JSON numbers beyond the representation of a float64.
const input = `[false, 1e-1000, 3.141592653589793238462643383279, 1e+1000, true]`
var value any
err := json.Unmarshal([]byte(input), &value,
// Intercept every attempt to unmarshal into the any type.
json.WithUnmarshalers(
json.UnmarshalFromFunc(func(dec *jsontext.Decoder, val *any) error {
// If the next value to be decoded is a JSON number,
// then provide a concrete Go type to unmarshal into.
if dec.PeekKind() == '0' {
*val = jsontext.Value(nil)
}
// Return ErrUnsupported to fallback on default unmarshal behavior.
return errors.ErrUnsupported
}),
))
if err != nil {
log.Fatal(err)
}
fmt.Println(value)
// Sanity check.
want := []any{false, jsontext.Value("1e-1000"), jsontext.Value("3.141592653589793238462643383279"), jsontext.Value("1e+1000"), true}
if !reflect.DeepEqual(value, want) {
log.Fatalf("value mismatch:\ngot %v\nwant %v", value, want)
}
Результат:
[false 1e-1000 3.141592653589793238462643383279 1e+1000 true]
Пример (RecordOffsets)
При использовании JSON для разбора файлов конфигурации логике разбора часто требуется сообщить об ошибке с указанием строки и столбца, где она возникла во входных данных.
Код:
// Hypothetical configuration file.
const input = `[
{"Source": "192.168.0.100:1234", "Destination": "192.168.0.1:80"},
{"Source": "192.168.0.251:4004"},
{"Source": "192.168.0.165:8080", "Destination": "0.0.0.0:80"}
]`
type Tunnel struct {
Source netip.AddrPort
Destination netip.AddrPort
// ByteOffset is populated during unmarshal with the byte offset
// within the JSON input of the JSON object for this Go struct.
ByteOffset int64 `json:"-"` // metadata to be ignored for JSON serialization
}
var tunnels []Tunnel
err := json.Unmarshal([]byte(input), &tunnels,
// Intercept every attempt to unmarshal into the Tunnel type.
json.WithUnmarshalers(
json.UnmarshalFromFunc(func(dec *jsontext.Decoder, tunnel *Tunnel) error {
// Decoder.InputOffset reports the offset after the last token,
// but we want to record the offset before the next token.
//
// Call Decoder.PeekKind to buffer enough to reach the next token.
// Add the number of leading whitespace, commas, and colons
// to locate the start of the next token.
dec.PeekKind()
unread := dec.UnreadBuffer()
n := len(unread) - len(bytes.TrimLeft(unread, " \n\r\t,:"))
tunnel.ByteOffset = dec.InputOffset() + int64(n)
// Return ErrUnsupported to fallback on default unmarshal behavior.
return errors.ErrUnsupported
}),
))
if err != nil {
log.Fatal(err)
}
// lineColumn converts a byte offset into a one-indexed line and column.
// The offset must be within the bounds of the input.
lineColumn := func(input string, offset int) (line, column int) {
line = 1 + strings.Count(input[:offset], "\n")
column = 1 + offset - (strings.LastIndex(input[:offset], "\n") + len("\n"))
return line, column
}
// Verify that the configuration file is valid.
for _, tunnel := range tunnels {
if !tunnel.Source.IsValid() || !tunnel.Destination.IsValid() {
line, column := lineColumn(input, int(tunnel.ByteOffset))
fmt.Printf("%d:%d: source and destination must both be specified", line, column)
}
}
Результат:
3:3: source and destination must both be specified
type SemanticError 1.27
SemanticError описывает ошибку при определении значения данных JSON как данных Go или наоборот.
Если метод Marshaler, MarshalerTo, Unmarshaler или UnmarshalerFrom, вызванный пакетом json, возвращает SemanticError, вызывающий контекст автоматически заполняет поля ByteOffset, JSONPointer и GoType, если они имеют нулевые значения.
Содержимое этой ошибки, формируемой данным пакетом, может со временем измениться.
type SemanticError struct {
// ByteOffset indicates that an error occurred at or after this byte offset.
ByteOffset int64
// JSONPointer indicates that an error occurred within this JSON value
// as indicated using the JSON Pointer notation (see RFC 6901).
JSONPointer jsontext.Pointer
// JSONKind is the JSON kind that could not be handled.
JSONKind jsontext.Kind // may be zero if unknown
// JSONValue is the JSON number or string that could not be unmarshaled.
// It is not populated during marshaling.
JSONValue jsontext.Value // may be nil if irrelevant or unknown
// GoType is the Go type that could not be handled.
GoType reflect.Type // may be nil if unknown
// Err is the underlying error.
Err error // may be nil
// contains filtered or unexported fields
}
func (*SemanticError) Error 1.27
func (e *SemanticError) Error() string
func (*SemanticError) Unwrap 1.27
func (e *SemanticError) Unwrap() error
type Unmarshaler 1.27
Unmarshaler реализуется типами, способными выполнять анмаршалинг самостоятельно. Рекомендуется реализовывать UnmarshalerFrom, если только реализация не пытается избежать прямой зависимости от пакета "jsontext".
Если метод вызывается функциями анмаршалинга этого пакета, входные данные можно считать корректной кодировкой значения JSON. При анмаршалинге в предварительно заполненное значение рекомендуется реализовывать в UnmarshalJSON семантику слияния, описанную в Unmarshal.
Реализации не должны сохранять или изменять входной []byte.
Реализации не должны возвращать errors.ErrUnsupported.
Если возвращенная ошибка является SemanticError, пакет json может заполнить незаполненные поля ошибки дополнительным контекстом. Ошибки других типов оборачиваются в SemanticError.
type Unmarshaler interface {
UnmarshalJSON([]byte) error
} type UnmarshalerFrom 1.27
UnmarshalerFrom реализуется типами, способными выполнять анмаршалинг самостоятельно. Рекомендуется реализовывать UnmarshalerFrom вместо Unmarshaler, поскольку этот интерфейс более производителен и гибок. Если тип реализует и Unmarshaler, и UnmarshalerFrom, приоритет имеет UnmarshalerFrom. В этом случае обе реализации должны стремиться к эквивалентному поведению при параметрах анмаршалинга по умолчанию.
Реализация должна считывать из Decoder только одно значение JSON. При анмаршалинге в предварительно заполненное значение рекомендуется реализовывать в UnmarshalJSONFrom семантику слияния, описанную в Unmarshal. В качестве альтернативы реализация может вернуть errors.ErrUnsupported, не изменяя Decoder. Пакет "json", вызывающий этот метод, использует следующее доступное представление JSON для типа-получателя. Реализации не должны сохранять указатель на jsontext.Decoder.
Если возвращенная ошибка является SemanticError, пакет json может заполнить незаполненные поля ошибки дополнительным контекстом. Ошибки других типов оборачиваются в SemanticError, за исключением ошибок [jsontext.SyntacticError] и ошибок ввода-вывода.
Не следует вызывать метод UnmarshalJSONFrom напрямую, поскольку он может возвращать sentinel-ошибки, требующие особой обработки. Вместо этого следует вызывать UnmarshalDecode, который обрабатывает такие случаи.
Реализациям следует проверять параметры анмаршалинга через jsontext.Decoder.Options и при необходимости изменять поведение с учетом этих параметров.
Для реализаций UnmarshalerFrom может быть важен следующий параметр:
- StringifyNumbers: если тип представлен как число JSON
Некоторые параметры, например FormatNilSliceAsNull, применяются только к встроенным типам Go. Поэтому обычно они не имеют прямого отношения к реализациям UnmarshalerFrom. Однако типы, представляющие составной тип, должны выполнять анмаршалинг содержащихся типов с помощью UnmarshalDecode, чтобы эти параметры применялись к содержащимся типам. Аналогично, WithUnmarshalers может влиять на анмаршалинг любого содержащегося типа в составе составного типа.
Все остальные параметры обрабатываются автоматически вне реализации UnmarshalerFrom и потому не имеют отношения к реализациям.
type UnmarshalerFrom interface {
UnmarshalJSONFrom(*jsontext.Decoder) error
} Пример
Для пользовательских типов можно задать собственное поведение анмаршалинга с помощью [UnmarshalerFrom].
Код:
s := "[1,2,3]"
var set IntSet
err := json.Unmarshal([]byte(s), &set)
if err != nil {
log.Fatal(err)
}
fmt.Println(set)
Результат:
map[1:{} 2:{} 3:{}]
type Unmarshalers 1.27
Unmarshalers — это список функций, которые могут переопределять поведение анмаршалинга для определенных типов. Заполните WithUnmarshalers, чтобы использовать этот список с Unmarshal, UnmarshalRead или UnmarshalDecode. Nil-указатель *Unmarshalers эквивалентен пустому списку. У Unmarshalers нет экспортируемых полей или методов.
type Unmarshalers = typedUnmarshalers
func JoinUnmarshalers 1.27
func JoinUnmarshalers(us ...*Unmarshalers) *Unmarshalers
JoinUnmarshalers создает плоский список функций анмаршалинга. Если для значения заданного типа применимы несколько функций из списка, приоритет имеют те, что расположены раньше. Если функция возвращает errors.ErrUnsupported, вызывается следующая подходящая функция; в противном случае используется поведение анмаршалинга по умолчанию.
Например:
u1 := JoinUnmarshalers(f1, f2) u2 := JoinUnmarshalers(f0, u1, f3) // equivalent to u3 u3 := JoinUnmarshalers(f0, f1, f2, f3) // equivalent to u2
func UnmarshalFromFunc 1.27
func UnmarshalFromFunc[T any](fn func(*jsontext.Decoder, T) error) *Unmarshalers
UnmarshalFromFunc создает типоспецифичный анмаршалер, задающий способ анмаршалинга значений типа T. T должен быть неименованным типом указателя или типом интерфейса. Функции всегда передается ненулевое значение указателя.
Реализации должны соответствовать требованиям UnmarshalerFrom.
Реализации не должны сохранять указатель на jsontext.Decoder или значение T.
func UnmarshalFunc 1.27
func UnmarshalFunc[T any](fn func([]byte, T) error) *Unmarshalers
UnmarshalFunc создает типоспецифичный анмаршалер, задающий способ анмаршалинга значений типа T. T должен быть неименованным типом указателя или типом интерфейса. Функции всегда передается ненулевое значение указателя.
Реализации должны соответствовать требованиям Unmarshaler.
Реализации не должны сохранять значение T.
© Google, Inc.
Licensed under the Creative Commons Attribution License 3.0.
http://golang.org/pkg/encoding/json/v2/