Слой хранения JSON
Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy — урок 102 из 107.
Задание
ЛегкоДавайте добавим сохранение данных в нашу систему управления библиотекой! Вы реализуете слой персистентности JSON, который сможет преобразовывать данные вашей библиотеки в формат JSON для сохранения и загружать их обратно из строк JSON. Это необходимо для любого реального приложения, которому нужно сохранять данные между сеансами.
Вы распределите свой код по пяти файлам:
book.go: Ваша структураBookс тегами JSON для управления сериализацией. Каждое поле должно иметь соответствующий тег JSON:id,title,author,isbnиavailable. Включите ваш конструкторNewBook.user.go: Ваша структураUserс тегами JSON дляid,name,emailиborrowed_books. Включите ваш конструкторNewUser.data.go: Создайте структуруLibraryData, которая будет выступать в качестве объекта передачи данных для сериализации JSON. Она должна иметь два поля:Books(срез указателей на Book) иUsers(срез указателей на User), каждое с соответствующими тегами JSON (booksиusers).library.go: Ваша структураLibraryс картами (maps) для книг и пользователей. Добавьте два новых метода:ToJSON()— преобразует данные библиотеки в отформатированную строку JSON, используяjson.MarshalIndentс отступом в два пробела. Он должен собрать все книги и пользователей из карт в структуруLibraryData, а затем сериализовать ее.FromJSON(jsonStr string)— принимает строку JSON, десериализует ее в структуруLibraryDataи заполняет карты библиотеки загруженными книгами и пользователями.
main.go: Продемонстрируйте слой персистентности, создав библиотеку и преобразовав ее в JSON.Считайте количество книг, затем для каждой книги считайте ее ID, название, автора и ISBN. Далее считайте количество пользователей, и для каждого пользователя считайте его ID, имя и email. После заполнения библиотеки вызовите
ToJSON()и выведите полученную строку JSON.
Будут предоставлены следующие входные данные:
- Количество книг, затем ID каждой книги, название, автор, ISBN (каждое на отдельной строке)
- Количество пользователей, затем ID каждого пользователя, имя, email (каждое на отдельной строке)
Например, при вводе:
2
B001
The Go Programming Language
Alan Donovan
978-0134190440
B002
Clean Code
Robert Martin
978-0132350884
1
U001
Alice Smith
alice@library.comВаш вывод должен быть таким:
{
"books": [
{
"id": "B001",
"title": "The Go Programming Language",
"author": "Alan Donovan",
"isbn": "978-0134190440",
"available": true
},
{
"id": "B002",
"title": "Clean Code",
"author": "Robert Martin",
"isbn": "978-0132350884",
"available": true
}
],
"users": [
{
"id": "U001",
"name": "Alice Smith",
"email": "alice@library.com",
"borrowed_books": []
}
]
}И при вводе:
1
B100
Design Patterns
Gang of Four
978-0201633610
0Ваш вывод должен быть таким:
{
"books": [
{
"id": "B100",
"title": "Design Patterns",
"author": "Gang of Four",
"isbn": "978-0201633610",
"available": true
}
],
"users": []
}Важно: При итерации по картам для создания срезов добавляйте книги в следующем порядке: B001, B002 и т. д. (отсортированные по ID в алфавитном порядке), и пользователей аналогично. Это обеспечит согласованный вывод JSON. Вы можете использовать sort.Strings для ключей карты перед итерацией.
Структура LibraryData устраняет разрыв между вашим хранилищем на основе карт и форматом JSON на основе массивов. Этот паттерн объекта передачи данных (DTO) часто встречается при сериализации сложных структур данных!
Попробуйте сами
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Создать новую библиотеку
library := NewLibrary()
// Прочитать количество книг
numBooksStr, _ := reader.ReadString('\n')
numBooks, _ := strconv.Atoi(strings.TrimSpace(numBooksStr))
// TODO: Прочитать данные каждой книги (ID, title, author, ISBN)
// и добавить их в библиотеку
for i := 0; i < numBooks; i++ {
// TODO: Прочитать ID книги
// TODO: Прочитать title книги
// TODO: Прочитать author книги
// TODO: Прочитать ISBN книги
// TODO: Создать книгу и добавить в библиотеку
}
// Прочитать количество пользователей
numUsersStr, _ := reader.ReadString('\n')
numUsers, _ := strconv.Atoi(strings.TrimSpace(numUsersStr))
// TODO: Прочитать данные каждого пользователя (ID, name, email)
// и добавить их в библиотеку
for i := 0; i < numUsers; i++ {
// TODO: Прочитать ID пользователя
// TODO: Прочитать name пользователя
// TODO: Прочитать email пользователя
// TODO: Создать пользователя и добавить в библиотеку
}
// TODO: Преобразовать библиотеку в JSON и вывести её
fmt.Println(library.ToJSON())
}
Все уроки раздела Объектно-ориентированное программирование
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 Pool12Продвинутые концепции ООП
Паттерн функциональных опцийПаттерн Builder в GoЦепочки вызовов методовПсевдонимы и определения типовОсновы рефлексииОбзор генерации кода15Проект: Управление библиотекой
Обзор и структура проектаСтруктуры Book и User