哈喽!大家好,很高兴又见面了,我是编程网的一名作者,今天由我给大家带来一篇《带指针的结构体序列化》,本文主要会讲到等等知识点,希望大家一起学习进步,也欢迎大家关注、点赞、收藏、转发! 下面就一起来看
哈喽!大家好,很高兴又见面了,我是编程网的一名作者,今天由我给大家带来一篇《带指针的结构体序列化》,本文主要会讲到等等知识点,希望大家一起学习进步,也欢迎大家关注、点赞、收藏、转发! 下面就一起来看看吧!
问题内容具有如下结构层次结构:
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
}
可以使用以下方法来完成:
代码将会运行,只是为了说明方法,我是 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 ))//.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
}
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相关知识,快来关注吧!
--结束END--
本文标题: 带指针的结构体序列化
本文链接: https://www.lsjlt.com/news/596096.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
下载Word文档到电脑,方便收藏和打印~
2024-04-05
2024-04-05
2024-04-05
2024-04-04
2024-04-05
2024-04-05
2024-04-05
2024-04-05
2024-04-04
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
一口价域名售卖能注册吗?域名是网站的标识,简短且易于记忆,为在线用户提供了访问我们网站的简单路径。一口价是在域名交易中一种常见的模式,而这种通常是针对已经被注册的域名转售给其他人的一种方式。
一口价域名买卖的过程通常包括以下几个步骤:
1.寻找:买家需要在域名售卖平台上找到心仪的一口价域名。平台通常会为每个可售的域名提供详细的描述,包括价格、年龄、流
443px" 443px) https://www.west.cn/docs/wp-content/uploads/2024/04/SEO图片294.jpg https://www.west.cn/docs/wp-content/uploads/2024/04/SEO图片294-768x413.jpg 域名售卖 域名一口价售卖 游戏音频 赋值/切片 框架优势 评估指南 项目规模
0