为了账号安全,请及时绑定邮箱和手机立即绑定

使用指针序列化结构

使用指针序列化结构

Go
天涯尽头无女友 2023-06-19 17:04:34
具有如下结构层次结构:type DomainStore struct {    Domains []*Domain    Users []*User}type Domain struct {    Name    string    Records []*Record    Owner   *User}type User struct {    Name      string    Email     string    Domains []*Domain}type Record struct {    Name      string    Host      string}使用具有域和用户列表的单个 DomainStore,以及域和用户之间的指针。我正在寻找一种对文件进行序列化/反序列化的方法。我一直在尝试使用 gob,但指针没有(按设计)序列化正确(扁平化)。考虑给每个对象一个唯一的 id 并制作一个函数来序列化/反序列化每种类型,但这似乎需要很多工作/样板。对策略有什么建议吗?我想将整个 DomainStore 保存在内存中,并根据用户请求序列化为文件。主要问题:如何序列化/反序列化并保持指针指向同一个对象而不是同一个对象的不同副本gob 和 json 似乎都“只是”复制对象的值并进行反序列化,我最终得到了多个独立的对象副本。使用 gob ang json 会发生以下情况:之前,A & C 都指向 B:A -> B <- C使用 json/gob 反序列化后:A -> B1 , C -> B2A & C 指向不同的对象,具有相同的值。但是,如果我更改 B1,它不会在 B2 中更改。- - 更新 - -编组时我可以获得对象的内存位置并将其用作 ID:func (u *User) MarshalJSON() ([]byte, error) {    return json.Marshal(&JsonUser{        ID:       fmt.Sprintf("%p", u),        Name:     u.Name,        Email:    u.Email,    })}在编组域时,我可以替换func (d *Domain) MarshalJSON() ([]byte, error) {    return json.Marshal(&struct {        ID       string `json:"id"`        Name     string `json:"name"`        User     string `json:"user"`    }{        ID:       fmt.Sprintf("%p", d),        Name:     d.Name,        User:     fmt.Sprintf("%p", d.User),    })}现在我只需要能够解组这个,这给我带来了一个问题,UnmarshalJSON 需要访问 id 及其各自对象的映射。func (u *User) UnmarshalJSON(data []byte) error {  // need acces to a map shared by all UnmarshalJSON functions}
查看完整描述

2 回答

?
慕森卡

TA贡献1806条经验 获得超8个赞

可以使用以下方法完成:

  1. 所有对象都放置在地图中的状态对象中。

  2. 当 State 对象中的对象被编组时,所有使用指针引用的对象都将替换为该对象的内存位置。

  3. 当使用先前读取的对象的全局列表恢复未编组的指针时。

代码将运行,只是为了说明方法,我是 Go 的新手,所以请耐心等待。

package main


import (

    "encoding/json"

    "errors"

    "fmt"

    "log"

    "strings"

)


type User struct {

    Name  string

    Email string

}

type JsonUser struct {

    ID    string `json:"id"`

    Name  string `json:"name"`

    Email string `json:"email"`

}


func (u *User) Print(level int) {

    ident := strings.Repeat("-", level)

    log.Println(ident, "Username:", u.Name, u.Email)

}

func (u *User) Id() string {

    return fmt.Sprintf("%p", u)

}

func (u *User) MarshalJSON() ([]byte, error) {

    return json.Marshal(&JsonUser{

        ID:    u.Id(),

        Name:  u.Name,

        Email: u.Email,

    })

}

func (u *User) UnmarshalJSON(data []byte) error {

    aux := &JsonUser{}

    if err := json.Unmarshal(data, &aux); err != nil {

        return err

    }

    u.Name = aux.Name

    u.Email = aux.Email

    load_helper[aux.ID] = u

    log.Println("Added user with id ", aux.ID, u.Name)

    return nil

}


type Record struct {

    Type     string // MX / A / CNAME / TXT / REDIR / SVR

    Name     string // @ / www

    Host     string // IP / address

    Priority int    // Used for MX

    Port     int    // Used for SVR

}

type JsonRecord struct {

    ID       string

    Type     string

    Name     string

    Host     string

    Priority int

    Port     int

}


func (r *Record) Print(level int) {

    ident := strings.Repeat("-", level)

    log.Println(ident, "", r.Type, r.Name, r.Host)

}

func (r *Record) Id() string {

    return fmt.Sprintf("%p", r)

}

func (r *Record) MarshalJSON() ([]byte, error) {

    return json.Marshal(&JsonRecord{

        ID:       r.Id(),

        Name:     r.Name,

        Type:     r.Type,

        Host:     r.Host,

        Priority: r.Priority,

        Port:     r.Port,

    })

}

func (r *Record) UnmarshalJSON(data []byte) error {

    aux := &JsonRecord{}

    if err := json.Unmarshal(data, &aux); err != nil {

        return err

    }

    r.Name = aux.Name

    r.Type = aux.Type

    r.Host = aux.Host

    r.Priority = aux.Priority

    r.Port = aux.Port

    load_helper[aux.ID] = r

    log.Println("Added record with id ", aux.ID, r.Name)

    return nil

}


type Domain struct {

    Name    string

    User    *User     // User ID

    Records []*Record // Record ID's

}

type JsonDomain struct {

    ID      string   `json:"id"`

    Name    string   `json:"name"`

    User    string   `json:"user"`

    Records []string `json:"records"`

}


func (d *Domain) Print(level int) {

    ident := strings.Repeat("-", level)

    log.Println(ident, "Domain:", d.Name)

    d.User.Print(level + 1)

    log.Println(ident, " Records:")

    for _, r := range d.Records {

        r.Print(level + 2)

    }

}

func (d *Domain) Id() string {

    return fmt.Sprintf("%p", d)

}

func (d *Domain) MarshalJSON() ([]byte, error) {

    var record_ids []string

    for _, r := range d.Records {

        record_ids = append(record_ids, r.Id())

    }

    return json.Marshal(JsonDomain{

        ID:      d.Id(),

        Name:    d.Name,

        User:    d.User.Id(),

        Records: record_ids,

    })

}

func (d *Domain) UnmarshalJSON(data []byte) error {

    log.Println("UnmarshalJSON domain")

    aux := &JsonDomain{}

    if err := json.Unmarshal(data, &aux); err != nil {

        return err

    }

    d.Name = aux.Name

    d.User = load_helper[aux.User].(*User) // restore pointer to domains user

    for _, record_id := range aux.Records {

        d.Records = append(d.Records, load_helper[record_id].(*Record))

    }

    return nil

}


type State struct {

    Users   map[string]*User

    Records map[string]*Record

    Domains map[string]*Domain

}


func NewState() *State {

    s := &State{}

    s.Users = make(map[string]*User)

    s.Domains = make(map[string]*Domain)

    s.Records = make(map[string]*Record)

    return s

}

func (s *State) Print() {

    log.Println("State:")

    log.Println("Users:")

    for _, u := range s.Users {

        u.Print(1)

    }

    log.Println("Domains:")

    for _, d := range s.Domains {

        d.Print(1)

    }

}

func (s *State) NewUser(name string, email string) *User {

    u := &User{Name: name, Email: email}

    id := fmt.Sprintf("%p", u)

    s.Users[id] = u

    return u

}

func (s *State) NewDomain(user *User, name string) *Domain {

    d := &Domain{Name: name, User: user}

    s.Domains[d.Id()] = d

    return d

}

func (s *State) NewMxRecord(d *Domain, rtype string, name string, host string, priority int) *Record {

    r := &Record{Type: rtype, Name: name, Host: host, Priority: priority}

    d.Records = append(d.Records, r)

    s.Records[r.Id()] = r

    return r

}

func (s *State) FindDomain(name string) (*Domain, error) {

    for _, v := range s.Domains {

        if v.Name == name {

            return v, nil

        }

    }

    return nil, errors.New("Not found")

}

func Save(s *State) (string, error) {

    b, err := json.MarshalIndent(s, "", "    ")

    if err == nil {

        return string(b), nil

    } else {

        log.Println(err)

        return "", err

    }

}


var load_helper map[string]interface{}


func Load(s *State, blob string) {

    load_helper = make(map[string]interface{})

    if err := json.Unmarshal([]byte(blob), s); err != nil {

        log.Println(err)

    } else {

        log.Println("OK")

    }

}


func test_state() {


    s := NewState()

    u := s.NewUser("Ownername", "some@email.com")

    d := s.NewDomain(u, "somedomain.com")

    s.NewMxRecord(d, "MX", "@", "192.168.1.1", 10)

    s.NewMxRecord(d, "A", "www", "192.168.1.1", 0)


    s.Print()


    x, _ := Save(s) // Saved to json string


    log.Println("State saved, the json string is:")

    log.Println(x)


    s2 := NewState() // Create a new empty State

    Load(s2, x)

    s2.Print()


    d, err := s2.FindDomain("somedomain.com")

    if err == nil {

        d.User.Name = "Changed"

    } else {

        log.Println("Error:", err)

    }

    s2.Print()

}


func main() {

    test_state()

}

这是相当多的代码,对象和序列化之间有很多耦合。全局变量 load_helper 也不好。改进的想法将不胜感激。


另一种方法是使用反射来制作更通用的解决方案。以下是使用此方法的示例:


package main


import (

    "encoding/json"

    "fmt"

    "log"

    "strings"

    "reflect"

)


func pprint(x interface{}) {

    b, err := json.MarshalIndent(x, "", "  ")

    if err != nil {

        fmt.Println("error:", err)

    }

    fmt.Println(string(b))  

}



var typeRegistry = make(map[string]reflect.Type)


// Register a type to make it possible for the Save/Load functions

// to serialize it.

func Register(v interface{}) {

    t := reflect.TypeOf(v)

    n := t.Name()

    fmt.Println("Register type",n)

    typeRegistry[n] = reflect.TypeOf(v)

}


// Make an instance of a type from the string name of the type.

func makeInstance(name string) reflect.Value {

    v := reflect.New(typeRegistry[name]).Elem()

    return v

}


// Translate a string type name tpo a real type.

func getTypeFromString(name string) reflect.Type {

    return typeRegistry[name]

}



// Serializeable interface must be supported by all objects passed to the Load / Save functions.

type Serializeable interface {

    Id() string

}


// GenericSave saves the object d

func GenericSave(d interface{}) (string, error) {

    r := make(map[string]interface{})

    v := reflect.ValueOf(d)

    t := reflect.TypeOf(d)

    if t.Kind()==reflect.Ptr {

        t=t.Elem()

        v=v.Elem()

    }

    r["_TYPE"]=t.Name()

    r["_ID"]=fmt.Sprintf("%p", d)

    for i := 0; i < t.NumField(); i++ {

        f := t.Field(i)

        name := f.Name

        vf := v.FieldByName(name)

//      fmt.Println("Field", i+1, "name is", name, "type is", f.Type.Name(), "and kind is", f.Type.Kind())      

//      fmt.Println("V:", vf)

        if f.Tag != "" {

            store:=strings.Split(f.Tag.Get("store"),",")

            switch store[1] {

            case "v":

                switch t.Field(i).Type.Name() {

                case "string":

                    r[store[0]]=vf.String()

                case "int":

                    r[store[0]]=vf.Int()

                }

            case "p":

                vals:=vf.MethodByName("Id").Call([]reflect.Value{})

                r[store[0]]=vals[0].String()

            case "lp":

                tr:=[]string{}

                for j := 0; j < vf.Len(); j++ {

                    vals:=vf.Index(j).MethodByName("Id").Call([]reflect.Value{})

                    tr=append(tr,vals[0].String())

                }

                r[store[0]]=tr

            }

        }

    }   

    m,_:=json.Marshal(r)

    return string(m),nil

}


// Save saves the list of objects.

func Save(objects []Serializeable) []byte {

    lst:=[]string{}

    for _,o := range(objects) {

        os,_:= GenericSave(o) // o.Save()

        lst=append(lst,os)

    }

    m,_:=json.Marshal(lst)

    return m

}



func toStructPtr(obj interface{}) interface{} {

    vp := reflect.New(reflect.TypeOf(obj))

    vp.Elem().Set(reflect.ValueOf(obj))

    return vp.Interface()

}


// Load creates a list of serializeable objects from json blob

func Load(blob []byte) []Serializeable {

    objects := []Serializeable{}

    loadHelper := make(map[string]interface{})

    var olist []interface{}

    if err := json.Unmarshal(blob, &olist); err != nil {

        log.Println(err)

    } else {

        for _,o := range(olist) {


            var omap map[string]interface{}

            json.Unmarshal([]byte(o.(string)), &omap)


            t:= getTypeFromString(omap["_TYPE"].(string))

            obj := reflect.New(t).Elem() 


            for i := 0; i < t.NumField(); i++ {

//              n:=t.Field(i).Name

//              fmt.Println(i,n,t.Field(i).Type.Name())


                if t.Field(i).Tag != "" {

                    store:=strings.Split(t.Field(i).Tag.Get("store"),",")

//                  fmt.Println(store)

                    switch store[1] {

                    case "v":

                        switch t.Field(i).Type.Name() {

                        case "string":

                            obj.FieldByIndex([]int{i}).SetString(omap[store[0]].(string))

                        case "int":

                            obj.FieldByIndex([]int{i}).SetInt(int64(omap[store[0]].(float64)))

                        }

                    case "p":

                        nObj:=loadHelper[omap[store[0]].(string)]

                        obj.FieldByIndex([]int{i}).Set(reflect.ValueOf(nObj.(*User)))

                    case "lp":

                        ptrItemType:=t.Field(i).Type.Elem()

                        slice := reflect.Zero(reflect.SliceOf(  ptrItemType /* reflect.TypeOf( &Record{} ) */  ))//.Interface()

                        for _, pID := range(omap[store[0]].([]interface{})) {

                            nObj:=loadHelper[pID.(string)]

                            slice=reflect.Append(slice,  reflect.ValueOf(nObj)  )

                        }

                        obj.FieldByIndex([]int{i}).Set(slice)                       

                    }

                }

            }

            oi:=toStructPtr(obj.Interface())

            oip:=oi.(Serializeable)

            objects=append(objects,oip)

            loadHelper[omap["_ID"].(string)]=oip

        }

    }

    return objects


}




/* Application data structures */


type User struct {

    Name  string `store:"name,v"`

    Email string `store:"email,v"`

}

func (u *User) Id() string {

    return fmt.Sprintf("%p", u)

}

func (u *User) Save() (string, error) {

    return GenericSave(u)

}

func (u *User) Print() {

    fmt.Println("User:",u.Name)

}



type Record struct {

    Type     string `store:"type,v"`// MX / A / CNAME / TXT / REDIR / SVR

    Name     string `store:"name,v"`// @ / www

    Host     string `store:"host,v"`// IP / address

    Priority int    `store:"priority,v"`// Used for MX

    Port     int    `store:"port,v"`// Used for SVR

}

func (r *Record) Id() string {

    return fmt.Sprintf("%p", r)

}

func (r *Record) Save() (string, error) {

    return GenericSave(r)

}

func (r *Record) Print() {

    fmt.Println("Record:",r.Type,r.Name,r.Host)

}



type Domain struct {

    Name    string    `store:"name,v"`

    User    *User     `store:"user,p"`    // User ID

    Records []*Record `store:"record,lp"` // Record ID's

}

func (d *Domain) Id() string {

    return fmt.Sprintf("%p", d)

}

func (d *Domain) Save() (string, error) {

    return GenericSave(d)

}

func (d *Domain) Print() {

    fmt.Println("Domain:",d.Name)

    d.User.Print()

    fmt.Println("Records:")

    for _, r := range d.Records {

        r.Print()

    }

}



type DBM struct {

    Domains []*Domain

    Users []*User

    Records []*Record

}

func (dbm *DBM) AddDomain(d *Domain) {

    dbm.Domains=append(dbm.Domains,d)

}

func (dbm *DBM) AddUser(u *User) {

    dbm.Users=append(dbm.Users,u)

}

func (dbm *DBM) AddRecord(r *Record) {

    dbm.Records=append(dbm.Records,r)

}

func (dbm *DBM) GetObjects() []Serializeable {

    objects:=[]Serializeable{}

    for _,r := range(dbm.Records) {

        objects=append(objects, r)

    }

    for _,u := range(dbm.Users) {

        objects=append(objects, u)

    }

    for _,d := range(dbm.Domains) {

        objects=append(objects, d)

    }

    return objects

}

func (dbm *DBM) SetObjects(objects []Serializeable) {

    for _,o := range(objects) {

        switch o.(type) {

        case *Record:

            fmt.Println("record")

            dbm.AddRecord(o.(*Record))

        case *User:

            fmt.Println("record")

            dbm.AddUser(o.(*User))

        case *Domain:

            fmt.Println("record")

            dbm.AddDomain(o.(*Domain))

        }

    }

}



func testState() {


    Register(User{})

    Register(Domain{})

    Register(Record{})


    dbm:=DBM{}


    u := &User{Name: "Martin", Email: "some@email.com"}

    dbm.AddUser(u)


    r1 := &Record{Name: "@", Type: "MX", Host: "mail.ishost.dk"}

    r2 := &Record{Name: "@", Type: "MX", Host: "mail.infoserv.dk"}

    dbm.AddRecord(r1)

    dbm.AddRecord(r2)


    d := &Domain{User:u, Name: "Martin", Records: []*Record{r1, r2}}

    dbm.AddDomain(d)


    x:=Save(dbm.GetObjects())


    fmt.Println("== Saved objects")

//  fmt.Println(string(x))


    fmt.Println("== Loading")


    dbm2:=DBM{}

    dbm2.SetObjects(Load(x))



    u2:=dbm2.Users[0]

    u2.Print()

    u2.Name="KURT"

    u2.Print()


    d2:=dbm2.Domains[0]

    d2.Print()

    d2.User.Name="ZIG"

    u2.Print()


}


func main() {

    testState()

}


查看完整回答
反对 回复 2023-06-19
?
开心每一天1111

TA贡献1836条经验 获得超13个赞

使用encoding/json包


元帅:


// Marshal is a function that marshals the object into an

// io.Reader.

// By default, it uses the JSON marshaller.

var Marshal = func(v interface{}) (io.Reader, error) {

  b, err := json.MarshalIndent(v, "", "\t")

  if err != nil {

    return nil, err

  }

  return bytes.NewReader(b), nil

}

解组:


// Unmarshal is a function that unmarshals the data from the

// reader into the specified value.

// By default, it uses the JSON unmarshaller.

var Unmarshal = func(r io.Reader, v interface{}) error {

  return json.NewDecoder(r).Decode(v)

}

不确定还有更多,


您可以做的另一件事是,将所有这些存储为 json 格式的字符串。


查看完整回答
反对 回复 2023-06-19
  • 2 回答
  • 0 关注
  • 108 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信