add: database to reflection decoder
This commit is contained in:
107
interfaces/11-reflection-decoder/database.go
Normal file
107
interfaces/11-reflection-decoder/database.go
Normal 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
2
interfaces/11-reflection-decoder/database.json
Normal file → Executable file
@ -51,4 +51,4 @@
|
|||||||
"Price": 150
|
"Price": 150
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
@ -8,9 +8,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -19,103 +17,8 @@ type item interface {
|
|||||||
fmt.Stringer
|
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
|
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 {
|
func (l list) String() string {
|
||||||
if len(l) == 0 {
|
if len(l) == 0 {
|
||||||
return "Sorry. We're waiting for delivery 🚚."
|
return "Sorry. We're waiting for delivery 🚚."
|
||||||
|
@ -15,36 +15,49 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// store := list{
|
var 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}},
|
|
||||||
// }
|
|
||||||
|
|
||||||
// out, err := json.MarshalIndent(store, "", "\t")
|
// db := database{list: &store}
|
||||||
// if err != nil {
|
// 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)
|
// log.Fatalln(err)
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// fmt.Println(string(out))
|
|
||||||
//---
|
|
||||||
data, err := ioutil.ReadFile("database.json")
|
data, err := ioutil.ReadFile("database.json")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln(err)
|
log.Fatalln(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var store list
|
|
||||||
if err = json.Unmarshal(data, &store); err != nil {
|
if err = json.Unmarshal(data, &store); err != nil {
|
||||||
log.Fatalln(err)
|
log.Fatalln(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Print(store)
|
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))
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user