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
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
@ -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 🚚."
|
||||
|
@ -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))
|
||||
*/
|
||||
}
|
||||
|
Reference in New Issue
Block a user