add: database to reflection decoder

This commit is contained in:
Inanc Gumus
2019-09-05 14:59:49 +03:00
parent ef9360c852
commit b67f113e24
4 changed files with 138 additions and 115 deletions

View File

@ -0,0 +1,107 @@
// For more tutorials: https://blog.learngoprogramming.com
//
// Copyright © 2018 Inanc Gumus
// Learn Go Programming Course
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
//
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"os"
"reflect"
)
type database struct {
list *list
types map[string]item
}
func (db *database) save() error {
data, err := json.MarshalIndent(db, "", "\t")
if err != nil {
log.Fatalln(err)
}
return ioutil.WriteFile("database.json", data, os.ModePerm)
}
func (db *database) load() error {
data, err := ioutil.ReadFile("database.json")
if err != nil {
return err
}
if err = json.Unmarshal(data, db); err != nil {
return err
}
return nil
}
func (db *database) MarshalJSON() ([]byte, error) {
type encodable struct {
Category string
Item item
}
var e []encodable
for _, it := range *db.list {
// TypeOf -> finds the dynamic type of "it"
// Elem -> returns the element type of the pointer
// Name -> returns the type name as string
cat := reflect.TypeOf(it).Elem().Name()
e = append(e, encodable{cat, it})
}
return json.Marshal(e)
}
func (db *database) UnmarshalJSON(data []byte) error {
var decodables []struct {
Category string
Item json.RawMessage
}
if err := json.Unmarshal(data, &decodables); err != nil {
return err
}
for _, d := range decodables {
it, err := db.newItem(d.Category)
if err != nil {
return err
}
if err := json.Unmarshal(d.Item, &it); err != nil {
return err
}
*db.list = append(*db.list, it)
}
return nil
}
func (db *database) register(category string, t item) {
if db.types == nil {
db.types = make(map[string]item)
}
db.types[category] = t
}
func (db *database) newItem(category string) (item, error) {
it, ok := db.types[category]
if !ok {
return nil, fmt.Errorf("register %q within database before decoding", category)
}
t := reflect.TypeOf(it).Elem()
v := reflect.New(t)
return v.Interface().(item), nil
}

2
interfaces/11-reflection-decoder/database.json Normal file → Executable file
View File

@ -51,4 +51,4 @@
"Price": 150
}
}
]
]

View File

@ -8,9 +8,7 @@
package main
import (
"encoding/json"
"fmt"
"reflect"
"strings"
)
@ -19,103 +17,8 @@ type item interface {
fmt.Stringer
}
// itemEncode adds an additional category field
// to the product types.
type itemEncode struct {
// Category is the product category field.
Category string
// Item is an interface value.
// Here, we are embedding an interface value.
Item item
}
// itemDecode is used to decode the product types from json.
type itemDecode struct {
// Category is the product category field.
Category string
// json.RawMessage is a special type of the json package.
// It prevents the json package functions encode/decode
// the specific parts of the json data.
Item json.RawMessage
}
type list []item
func (l list) MarshalJSON() ([]byte, error) {
var eitems []itemEncode
for _, it := range l {
// TypeOf -> finds the dynamic type of "it"
// Elem -> returns the element type of the pointer
// Name -> returns the type name as string
cat := reflect.TypeOf(it).Elem().Name()
eitems = append(eitems, itemEncode{cat, it})
}
return json.Marshal(eitems)
}
func (l *list) UnmarshalJSON(data []byte) error {
var ditems []itemDecode
if err := json.Unmarshal(data, &ditems); err != nil {
return err
}
items, err := decode(ditems)
if err != nil {
return err
}
*l = items
return nil
}
// decode and return items as a list.
func decode(data []itemDecode) (items list, err error) {
for _, d := range data {
item := newItem(d.Category)
if json.Unmarshal(d.Item, &item); err != nil {
return nil, err
}
items = append(items, item)
}
return items, nil
}
// newItem creates and returns an item depending on category.
func newItem(category string) item {
// for each product you need to open this code
// and add a new case.
//
// dependency injection should be the responsibility
// of the user of this function.
switch category {
case "book":
// var b book
// return &b
return new(book)
case "game":
return new(game)
case "puzzle":
return new(puzzle)
case "toy":
return new(toy)
}
return nil
}
// type decoder map[string]interface{}
// func (d decoder) newItem(kind string) item {
// t := reflect.TypeOf(d[kind])
// v := reflect.New(t)
// return v.Interface().(item)
// }
func (l list) String() string {
if len(l) == 0 {
return "Sorry. We're waiting for delivery 🚚."

View File

@ -15,36 +15,49 @@ import (
)
func main() {
// store := list{
// &book{product{"moby dick", 10}, toTimestamp(118281600)},
// &book{product{"odyssey", 15}, toTimestamp("733622400")},
// &book{product{"hobbit", 25}, unknown},
// &puzzle{product{"rubik's cube", 5}},
// &game{product{"minecraft", 20}},
// &game{product{"tetris", 5}},
// &toy{product{"yoda", 150}},
// }
var store list
// out, err := json.MarshalIndent(store, "", "\t")
// if err != nil {
// db := database{list: &store}
// db.register("book", new(book))
// db.register("game", new(game))
// db.register("puzzle", new(puzzle))
// db.register("toy", new(toy))
// if err := db.load(); err != nil {
// log.Fatalln(err)
// }
// fmt.Println(string(out))
//---
data, err := ioutil.ReadFile("database.json")
if err != nil {
log.Fatalln(err)
}
var store list
if err = json.Unmarshal(data, &store); err != nil {
log.Fatalln(err)
}
fmt.Print(store)
// for _, p := range store {
// fmt.Printf("%#v\n", p)
// }
/*
store := list{
&book{product{"moby dick", 10}, toTimestamp(118281600)},
&book{product{"odyssey", 15}, toTimestamp("733622400")},
&book{product{"hobbit", 25}, unknown},
&puzzle{product{"rubik's cube", 5}},
&game{product{"minecraft", 20}},
&game{product{"tetris", 5}},
&toy{product{"yoda", 150}},
}
db := &database{list: &store}
if err := db.save(); err != nil {
log.Fatalln(err)
}
// out, err := json.MarshalIndent(db, "", "\t")
// if err != nil {
// log.Fatalln(err)
// }
// fmt.Println(string(out))
*/
}