encoding/json со структурами
Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy — урок 76 из 107.
Пакет encoding/json позволяет преобразовывать структуры Go в JSON и обратно. Это необходимо для API, конфигурационных файлов и хранения данных. Теги структур управляют тем, как поля сопоставляются с ключами JSON.
Чтобы преобразовать структуру в JSON, используйте json.Marshal:
type User struct {
Name string `json:"name"`
Email string `json:"email"`
Age int `json:"age"`
}
func main() {
user := User{Name: "Alice", Email: "alice@example.com", Age: 30}
data, _ := json.Marshal(user)
fmt.Println(string(data))
// {"name":"Alice","email":"alice@example.com","age":30}
}Теги структур, такие как `json:"name"`, определяют имена ключей JSON. Без тегов Go использует имя поля напрямую. В вывод JSON включаются только экспортируемые поля (начинающиеся с заглавной буквы).
Чтобы преобразовать JSON обратно в структуру, используйте json.Unmarshal:
jsonStr := `{"name":"Bob","email":"bob@example.com","age":25}`
var user User
json.Unmarshal([]byte(jsonStr), &user)
fmt.Println(user.Name) // BobРаспространенные параметры тегов включают omitempty для пропуска нулевых значений и - для полного игнорирования поля:
type Product struct {
ID int `json:"id"`
Name string `json:"name"`
Price float64 `json:"price,omitempty"`
Internal string `json:"-"`
}С использованием omitempty, если Price равно нулю, оно не появится в JSON. Поле Internal никогда не включается, независимо от его значения.
Задание
ЛегкоДавайте создадим систему каталога фильмов, которая преобразует структуры Go в JSON и наоборот! Вы попрактикуетесь в маршалинге структур в JSON и анмаршалинге JSON обратно в структуры, используя теги структур для управления именами полей JSON.
Вы организуете свой код в двух файлах:
movie.go: Определите модель данных фильма с соответствующими тегами JSON.Создайте структуру
Movieсо следующими полями и сопоставлениями JSON:Title(string) сопоставляется с"title"Director(string) сопоставляется с"director"Year(int) сопоставляется с"year"Rating(float64) сопоставляется с"rating"с параметромomitempty, чтобы у фильмов без рейтинга поле рейтинга не отображалосьInternalID(string) должен быть полностью исключен из JSON с помощью"-"
Создайте функцию
ToJSON(m Movie) string, которая преобразует Movie в строку JSON. Также создайтеFromJSON(jsonStr string) Movie, которая разбирает строку JSON обратно в структуру Movie.main.go: Продемонстрируйте преобразование JSON в обоих направлениях.Считайте тип операции (
"encode"или"decode").Для
"encode": Считайте данные фильма (название, режиссер, год, рейтинг) на отдельных строках. Создайте Movie с InternalID равным"internal-123", преобразуйте его в JSON и выведите результат.Для
"decode": Считайте строку JSON, преобразуйте ее в структуру Movie и выведите данные фильма в следующем формате:Title: [title] Director: [director] Year: [year] Rating: [rating]Выведите рейтинг с одним десятичным знаком. Если рейтинг равен 0 (отсутствует в JSON), выведите вместо него
Rating: N/A.
Будут предоставлены следующие входные данные:
- Строка 1: Тип операции (
"encode"или"decode") - Для encode: название, режиссер, год, рейтинг (четыре строки)
- Для decode: строка JSON (одна строка)
Например, при вводе:
encode
Inception
Christopher Nolan
2010
8.8Ваш вывод должен быть:
{"title":"Inception","director":"Christopher Nolan","year":2010,"rating":8.8}Обратите внимание, что InternalID не появляется в выводе из-за тега "-".
И при вводе:
encode
The Matrix
Wachowskis
1999
0Ваш вывод должен быть:
{"title":"The Matrix","director":"Wachowskis","year":1999}Обратите внимание, что rating опущен, так как он равен нулю и имеет тег omitempty.
И при вводе:
decode
{"title":"Interstellar","director":"Christopher Nolan","year":2014,"rating":8.6}Ваш вывод должен быть:
Title: Interstellar
Director: Christopher Nolan
Year: 2014
Rating: 8.6И при вводе:
decode
{"title":"Tenet","director":"Christopher Nolan","year":2020}Ваш вывод должен быть:
Title: Tenet
Director: Christopher Nolan
Year: 2020
Rating: N/AШпаргалка
Пакет encoding/json преобразует структуры Go в JSON и наоборот с помощью json.Marshal и json.Unmarshal.
Чтобы преобразовать структуру в JSON:
type User struct {
Name string `json:"name"`
Email string `json:"email"`
Age int `json:"age"`
}
user := User{Name: "Alice", Email: "alice@example.com", Age: 30}
data, _ := json.Marshal(user)
fmt.Println(string(data))
// {"name":"Alice","email":"alice@example.com","age":30}Чтобы преобразовать JSON обратно в структуру:
jsonStr := `{"name":"Bob","email":"bob@example.com","age":25}`
var user User
json.Unmarshal([]byte(jsonStr), &user)
fmt.Println(user.Name) // BobТеги структур управляют сопоставлением полей JSON:
`json:"name"`— сопоставляет поле с ключом JSON "name"`json:"price,omitempty"`— пропускает поле, если его значение является нулевым (zero value)`json:"-"`— полностью исключает поле из JSON
type Product struct {
ID int `json:"id"`
Name string `json:"name"`
Price float64 `json:"price,omitempty"`
Internal string `json:"-"`
}В вывод JSON включаются только экспортируемые поля (начинающиеся с заглавной буквы).
Попробуйте сами
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Чтение типа операции
operation, _ := reader.ReadString('\n')
operation = strings.TrimSpace(operation)
if operation == "encode" {
// Чтение данных о фильме
title, _ := reader.ReadString('\n')
title = strings.TrimSpace(title)
director, _ := reader.ReadString('\n')
director = strings.TrimSpace(director)
yearStr, _ := reader.ReadString('\n')
yearStr = strings.TrimSpace(yearStr)
year, _ := strconv.Atoi(yearStr)
ratingStr, _ := reader.ReadString('\n')
ratingStr = strings.TrimSpace(ratingStr)
rating, _ := strconv.ParseFloat(ratingStr, 64)
// TODO: Создать структуру Movie с прочитанными значениями
// Установить InternalID в значение "internal-123"
// Преобразовать в JSON с помощью ToJSON и вывести результат
} else if operation == "decode" {
// Чтение строки JSON
jsonStr, _ := reader.ReadString('\n')
jsonStr = strings.TrimSpace(jsonStr)
// TODO: Преобразовать JSON в Movie с помощью FromJSON
// Вывести данные о фильме в требуемом формате
// Использовать fmt.Sprintf("%.1f", rating) для форматирования рейтинга
// Если рейтинг равен 0, вывести "Rating: N/A" вместо него
}
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП в Go
Внешние файлыРабочее пространство и модули GoПакеты и импортыЭкспортируемые и неэкспортируемые именаВведение в ООП в GoСтруктуры как классыОпределение методов структурПолучатели-указатели и получатели-значенияИнициализация структурФункции-конструкторыИтоги — Простой калькулятор4Интерфейсы
Введение в интерфейсыНеявная реализацияИнтерфейс как контрактПустой интерфейс (any)Утверждение типаПереключатель типовКомпозиция интерфейсовИнтерфейсы Stringer и ErrorПовторение: Калькулятор фигур7Инкапсуляция
Экспортируемые и неэкспортируемые поляИнкапсуляция на уровне пакетовГеттеры и сеттерыСокрытие информации в GoИтоги — Записи о студентах10Обобщения (Generics) (Go 1.18+)
Введение в GenericsПараметры типовОграничения типовОбобщенные структурыОбходной путь для обобщенных методовИтоги — Обобщенная коллекция13Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн Abstract FactoryПаттерн ObserverПаттерн Strategy2Глубокое погружение в типы и структуры
Базовые и составные типыОпределение пользовательских типовТеги структурАнонимные структурыВложенные структурыНулевые значения и значения по умолчаниюПовторение — Контактная книга5Композиция вместо наследования
Почему в Go нет наследованияОсновы встраивания структурПродвижение методовВстраивание нескольких структурВстраивание против агрегацииЗатенение встроенных методовИтоги — Иерархия сотрудников8Обработка ошибок и ООП
Интерфейс errorПользовательские типы ошибокОбертывание ошибок (fmt.Errorf)Sentinel-ошибкиerrors.Is() и errors.As()Panic, Defer и RecoverИтоги — Парсер файлов11Стандартная библиотека и ООП
io.Reader и io.Writersort.InterfaceИнтерфейс fmt.Stringerencoding/json со структурамиИнтерфейс http.HandlerПовторение: модели REST API14Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикMiddleware как Декоратор3Указатели и память
Основы указателей в GoУказатели на структурыПередача по значению и по ссылкеФункция new()Сборка мусора в GoПовторение: Конструктор связного списка6Полиморфизм в Go
Полиморфизм через интерфейсыУтиная типизация в GoПравила реализации интерфейсовПолиморфные коллекцииВнедрение зависимостейИтоги — Обработчик платежей9Конкурентность и ООП
Основы горутинКаналы и взаимодействиеБуферизованные и небуферизованные каналыОператор selectsync.Mutex и sync.RWMutexsync.WaitGroupПроектирование потокобезопасных структурПовторение — Worker Pool