Menu
Coddy logo textTech

JSON Persistence Layer

Part of the Object Oriented Programming section of Coddy's GO journey — lesson 102 of 107.

challenge icon

Challenge

Easy

Let's add data persistence to our Library Management System! You'll implement a JSON persistence layer that can convert your library's data to JSON format for saving, and load it back from JSON strings. This is essential for any real application that needs to preserve data between sessions.

You'll organize your code across five files:

  • book.go: Your Book struct with JSON struct tags to control serialization. Each field should have a corresponding JSON tag: id, title, author, isbn, and available. Include your NewBook constructor.
  • user.go: Your User struct with JSON struct tags for id, name, email, and borrowed_books. Include your NewUser constructor.
  • data.go: Create a LibraryData struct that acts as a transfer object for JSON serialization. It should have two fields: Books (a slice of Book pointers) and Users (a slice of User pointers), each with appropriate JSON tags (books and users).
  • library.go: Your Library struct with maps for books and users. Add two new methods:
    • ToJSON() — converts the library's data to a formatted JSON string using json.MarshalIndent with two-space indentation. It should collect all books and users from the maps into a LibraryData struct, then serialize it.
    • FromJSON(jsonStr string) — takes a JSON string, unmarshals it into a LibraryData struct, and populates the library's maps with the loaded books and users.
  • main.go: Demonstrate the persistence layer by building a library and converting it to JSON.

    Read the number of books, then for each book read its ID, title, author, and ISBN. Next, read the number of users, and for each user read their ID, name, and email. After populating the library, call ToJSON() and print the resulting JSON string.

The following inputs will be provided:

  • Number of books, then each book's ID, title, author, ISBN (each on separate lines)
  • Number of users, then each user's ID, name, email (each on separate lines)

For example, given:

2
B001
The Go Programming Language
Alan Donovan
978-0134190440
B002
Clean Code
Robert Martin
978-0132350884
1
U001
Alice Smith
alice@library.com

Your output should be:

{
  "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": []
    }
  ]
}

And given:

1
B100
Design Patterns
Gang of Four
978-0201633610
0

Your output should be:

{
  "books": [
    {
      "id": "B100",
      "title": "Design Patterns",
      "author": "Gang of Four",
      "isbn": "978-0201633610",
      "available": true
    }
  ],
  "users": []
}

Important: When iterating over maps to build your slices, add books in the order: B001, B002, etc. (sorted by ID alphabetically) and users similarly. This ensures consistent JSON output. You can use sort.Strings on the map keys before iterating.

The LibraryData struct bridges the gap between your map-based storage and JSON's array-based format. This transfer object pattern is common when serializing complex data structures!

Try it yourself

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)

	// Create a new library
	library := NewLibrary()

	// Read number of books
	numBooksStr, _ := reader.ReadString('\n')
	numBooks, _ := strconv.Atoi(strings.TrimSpace(numBooksStr))

	// TODO: Read each book's details (ID, title, author, ISBN)
	// and add them to the library
	for i := 0; i < numBooks; i++ {
		// TODO: Read book ID
		// TODO: Read book title
		// TODO: Read book author
		// TODO: Read book ISBN
		// TODO: Create book and add to library
	}

	// Read number of users
	numUsersStr, _ := reader.ReadString('\n')
	numUsers, _ := strconv.Atoi(strings.TrimSpace(numUsersStr))

	// TODO: Read each user's details (ID, name, email)
	// and add them to the library
	for i := 0; i < numUsers; i++ {
		// TODO: Read user ID
		// TODO: Read user name
		// TODO: Read user email
		// TODO: Create user and add to library
	}

	// TODO: Convert library to JSON and print it
	fmt.Println(library.ToJSON())
}

All lessons in Object Oriented Programming