Формат обмена данными JSON удобен для чтения и записи человеком и эффективен для анализа и генерации машинами.
Типы по умолчанию
Типы Go по умолчанию для декодирования и кодирования JSON:
- bool для логических выражений JSON
- float64 для JSON чисел
- string JSON для строк
- nil для JSON null
Кроме того, time.Time и числовые типы в пакете math/big могут автоматически кодироваться как строки JSON.
Обратите внимание, что JSON не поддерживает базовые целочисленные типы. Они часто могут быть аппроксимированы числами с плавающей точкой.
Кодировать (marshal) структуру в JSON
Функция json.Marshal в пакете encoding/json генерирует данные JSON.
type FruitBasket struct {
Name string
Fruit []string
Id int64 `json:"ref"`
private string // Неэкспортируемое поле не кодируется.
Created time.Time
}
basket := FruitBasket{
Name: "Standard",
Fruit: []string{"Apple", "Banana", "Orange"},
Id: 999,
private: "Second-rate",
Created: time.Now(),
}
var jsonData []byte
jsonData, err := json.Marshal(basket)
if err != nil {
log.Println(err)
}
fmt.Println(string(jsonData))
Вывод:
{"Name":"Standard","Fruit":["Apple","Banana","Orange"],"ref":999,"Created":"2018-04-09T23:00:00Z"}
Только данные, которые могут быть представлены как JSON, будут закодированы.
- Только экспортируемые (публичные) поля структуры будут присутствовать в выводе JSON. Другие поля игнорируются.
- Поле с тегом json: сохраняется с именем тега вместо имени поля структуры.
- Указатели будут закодированы как значения, на которые они указывают, или равны null, если указатель равен nil.
Отформатированная печать
Замените json.Marshal на json.MarshalIndent в примере выше, чтобы сделать отступ для вывода JSON.
jsonData, err := json.MarshalIndent(basket, "", " ")
Вывод:
{
"Name": "Standard",
"Fruit": [
"Apple",
"Banana",
"Orange"
],
"ref": 999,
"Created": "2018-04-09T23:00:00Z"
}
Декодировать (unmarshal) JSON в структуру
Функция json.Unmarshal в пакете encoding/json декодирует данные JSON.
type FruitBasket struct {
Name string
Fruit []string
Id int64 `json:"ref"`
Created time.Time
}
jsonData := []byte(`
{
"Name": "Standard",
"Fruit": [
"Apple",
"Banana",
"Orange"
],
"ref": 999,
"Created": "2018-04-09T23:00:00Z"
}`)
var basket FruitBasket
err := json.Unmarshal(jsonData, &basket)
if err != nil {
log.Println(err)
}
fmt.Println(basket.Name, basket.Fruit, basket.Id)
fmt.Println(basket.Created)
Вывод:
Standard [Apple Banana Orange] 999
2018-04-09 23:00:00 +0000 UTC
Обратите внимание, что Unmarshal выделил новый срез самостоятельно. Вот как работает демаршалинг для срезов, карт и указателей.
Для заданного JSON ключа Foo Unmarshal попытается сопоставить поля структуры в следующем порядке:
- экспортированное (публичное) поле с тегом json: "Foo"
- экспортированное поле с именем Foo
- экспортированное поле с именем FOO, FoO или другим сопоставлением без учета регистра
Только поля, которые найдены в типе назначения, будут декодированы:
- Это полезно, когда вы хотите выбрать только несколько конкретных полей.
- В частности, любые неэкспортированные поля в структуре назначения не будут затронуты.
Произвольные объекты и массивы
Пакет encoding/json использует
- map[string]interface{} для хранения произвольных объектов JSON
- []interface{} для хранения произвольных массивов JSON
Любые допустимые данные JSON будут декодированы в обычном interface{} значении.
Рассмотрим данные JSON:
{
"Name": "Eve",
"Age": 6,
"Parents": [
"Alice",
"Bob"
]
}
Функция json.Unmarshal будет декодировать их в карту, ключи которой являются строками, а сами значения хранятся как пустые значения интерфейса:
map[string]interface{}{
"Name": "Eve",
"Age": 6.0,
"Parents": []interface{}{
"Alice",
"Bob",
},
}
Мы можем перебрать карту с помощью оператора range и использовать переключатель типа для доступа к ее значениям.
jsonData := []byte(`{"Name":"Eve","Age":6,"Parents":["Alice","Bob"]}`)
var v interface{}
json.Unmarshal(jsonData, &v)
data := v.(map[string]interface{})
for k, v := range data {
switch v := v.(type) {
case string:
fmt.Println(k, v, "(string)")
case float64:
fmt.Println(k, v, "(float64)")
case []interface{}:
fmt.Println(k, "(array):")
for i, u := range v {
fmt.Println(" ", i, u)
}
default:
fmt.Println(k, v, "(unknown)")
}
}
Вывод:
Name Eve (string)
Age 6 (float64)
Parents (array):
0 Alice
1 Bob
Пример JSON файла
Типы json.Decoder и json.Encoder в пакете encoding/json обеспечивают поддержку чтения и записи потоков данных JSON, например, файлов.
Код в этом примере
- читает поток объектов JSON из Reader (strings.Reader),
- удаляет поле Age из каждого объекта,
- и затем записывает объекты в Writer (os.Stdout).
const jsonData = `
{"Name": "Alice", "Age": 25}
{"Name": "Bob", "Age": 22}
`
reader := strings.NewReader(jsonData)
writer := os.Stdout
dec := json.NewDecoder(reader)
enc := json.NewEncoder(writer)
for {
// Чтение одного объекта JSON и сохранение его в карте.
var m map[string]interface{}
if err := dec.Decode(&m); err == io.EOF {
break
} else if err != nil {
log.Fatal(err)
}
// Удаление все пар ключ-значение
// с ключом == "Age" из карты.
for k := range m {
if k == "Age" {
delete(m, k)
}
}
//Запись карты как объект JSON.
if err := enc.Encode(&m); err != nil {
log.Println(err)
}
}
Вывод:
{"Name":"Alice"}
{"Name":"Bob"}
Читайте также:
- Работа с JSON в Golang
- Основы Go: структуры (struct)
- Основы Go: карты (словари)
- Основы Go: switch с типами
Комментариев нет:
Отправить комментарий