当前位置:首页 > 文章列表 > Golang > Go问答 > 带指针的结构体序列化

带指针的结构体序列化

来源:stackoverflow 2024-03-30 14:42:34 0浏览 收藏

哈喽!大家好,很高兴又见面了,我是golang学习网的一名作者,今天由我给大家带来一篇《带指针的结构体序列化》,本文主要会讲到等等知识点,希望大家一起学习进步,也欢迎大家关注、点赞、收藏、转发! 下面就一起来看看吧!

问题内容

具有如下结构层次结构:

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 -> b2

a 和 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
}

解决方案


可以使用以下方法来完成:

  1. 所有对象都放置在 state 对象的地图中。
  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", "[email protected]")
    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: "[email protected]"}
    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()
}

使用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 格式的字符串。

终于介绍完啦!小伙伴们,这篇关于《带指针的结构体序列化》的介绍应该让你收获多多了吧!欢迎大家收藏或分享给更多需要学习的朋友吧~golang学习网公众号也会发布Golang相关知识,快来关注吧!

版本声明
本文转载于:stackoverflow 如有侵犯,请联系study_golang@163.com删除
PHP.ini报错排查指南:五种常见错误及解决方法PHP.ini报错排查指南:五种常见错误及解决方法
上一篇
PHP.ini报错排查指南:五种常见错误及解决方法
UniApp实现动画效果的配置与使用方法
下一篇
UniApp实现动画效果的配置与使用方法
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    542次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    508次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    497次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    484次学习
查看更多
AI推荐
  • 毕业宝AIGC检测:AI生成内容检测工具,助力学术诚信
    毕业宝AIGC检测
    毕业宝AIGC检测是“毕业宝”平台的AI生成内容检测工具,专为学术场景设计,帮助用户初步判断文本的原创性和AI参与度。通过与知网、维普数据库联动,提供全面检测结果,适用于学生、研究者、教育工作者及内容创作者。
    18次使用
  • AI Make Song:零门槛AI音乐创作平台,助你轻松制作个性化音乐
    AI Make Song
    AI Make Song是一款革命性的AI音乐生成平台,提供文本和歌词转音乐的双模式输入,支持多语言及商业友好版权体系。无论你是音乐爱好者、内容创作者还是广告从业者,都能在这里实现“用文字创造音乐”的梦想。平台已生成超百万首原创音乐,覆盖全球20个国家,用户满意度高达95%。
    29次使用
  • SongGenerator.io:零门槛AI音乐生成器,快速创作高质量音乐
    SongGenerator
    探索SongGenerator.io,零门槛、全免费的AI音乐生成器。无需注册,通过简单文本输入即可生成多风格音乐,适用于内容创作者、音乐爱好者和教育工作者。日均生成量超10万次,全球50国家用户信赖。
    27次使用
  •  BeArt AI换脸:免费在线工具,轻松实现照片、视频、GIF换脸
    BeArt AI换脸
    探索BeArt AI换脸工具,免费在线使用,无需下载软件,即可对照片、视频和GIF进行高质量换脸。体验快速、流畅、无水印的换脸效果,适用于娱乐创作、影视制作、广告营销等多种场景。
    30次使用
  • SEO标题协启动:AI驱动的智能对话与内容生成平台 - 提升创作效率
    协启动
    SEO摘要协启动(XieQiDong Chatbot)是由深圳协启动传媒有限公司运营的AI智能服务平台,提供多模型支持的对话服务、文档处理和图像生成工具,旨在提升用户内容创作与信息处理效率。平台支持订阅制付费,适合个人及企业用户,满足日常聊天、文案生成、学习辅助等需求。
    32次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码