From b67f113e24fc9c63bfb0c2eec5ddda852e84bcdf Mon Sep 17 00:00:00 2001 From: Inanc Gumus Date: Thu, 5 Sep 2019 14:59:49 +0300 Subject: [PATCH] add: database to reflection decoder --- interfaces/11-reflection-decoder/database.go | 107 ++++++++++++++++++ .../11-reflection-decoder/database.json | 2 +- interfaces/11-reflection-decoder/list.go | 97 ---------------- interfaces/11-reflection-decoder/main.go | 47 +++++--- 4 files changed, 138 insertions(+), 115 deletions(-) create mode 100644 interfaces/11-reflection-decoder/database.go mode change 100644 => 100755 interfaces/11-reflection-decoder/database.json diff --git a/interfaces/11-reflection-decoder/database.go b/interfaces/11-reflection-decoder/database.go new file mode 100644 index 0000000..948e3b7 --- /dev/null +++ b/interfaces/11-reflection-decoder/database.go @@ -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 +} diff --git a/interfaces/11-reflection-decoder/database.json b/interfaces/11-reflection-decoder/database.json old mode 100644 new mode 100755 index 5caa227..201fcfc --- a/interfaces/11-reflection-decoder/database.json +++ b/interfaces/11-reflection-decoder/database.json @@ -51,4 +51,4 @@ "Price": 150 } } -] +] \ No newline at end of file diff --git a/interfaces/11-reflection-decoder/list.go b/interfaces/11-reflection-decoder/list.go index 3fa08f9..ff0b6a9 100644 --- a/interfaces/11-reflection-decoder/list.go +++ b/interfaces/11-reflection-decoder/list.go @@ -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 🚚." diff --git a/interfaces/11-reflection-decoder/main.go b/interfaces/11-reflection-decoder/main.go index 98d3312..b6693e2 100644 --- a/interfaces/11-reflection-decoder/main.go +++ b/interfaces/11-reflection-decoder/main.go @@ -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)) + */ }