go - 如何在go中保存和加载接口结构
问题描述
来自 python,我习惯于用自定义方法编写一个类。启动它,保存对象并再次加载它。我正在尝试在 go 中完成类似的事情。在阅读了关于序列化的文章后,我尝试使用 marshall/unmarshall 方法。
但是下面的代码 deos 不起作用,因为它导致 method.Method = nil。我希望能够调用 method.Method.Calculcate()
下面是我的尝试。
import (
"encoding/json"
"fmt"
"log"
)
type Calculator struct {
Method Method `json:"Method"`
}
type method1 struct {
Input string `json:"input"`
}
func NewMethod1(input string) Method {
return &method1{
Input: input,
}
}
type method2 struct {
Input string `json:"input"`
}
func NewMethod2(input string) Method {
return &method2{
Input: input,
}
}
type Method interface {
Calculcate()
}
func (m *method1) Calculcate() {
}
func (m *method2) Calculcate() {
}
func main() {
model := Calculator{
Method: NewMethod1("inputData"),
}
model.Method.Calculcate()
var jsonData []byte
jsonData, err := json.Marshal(model)
if err != nil {
log.Println(err)
}
fmt.Println(string(jsonData))
var method Calculator
json.Unmarshal(jsonData, &method)
if err != nil {
log.Println(err)
}
}
我想加载结构并在计算时使用相同的代码来启动方法 1 和方法 2。这意味着我事先不知道我是在加载方法 1 还是方法 2。下面是一点点伪代码,解释了我想要什么。
Calculator :=Load Calculator()
model := Calculator{
Method: NewMethod1("inputData"),
}
model.Method.Calculcate()
解决方案
问题是当字段具有接口类型(多种类型可能实现一个接口)时,解码 JSON 不知道使用什么具体类型。在您的示例中可能很明显,但请再想一想:您将此 JSON 发送到另一台计算机尝试对其进行解码。JSON 表示不记录接口值的具体类型(在本例中为*main.method1
),因此另一端不知道要为接口字段实例化什么Calculator.Method
。
如果需要这种序列化和反序列化,请改用encoding/gob
包。该包还写入类型信息,因此解码器部分至少知道类型名称。encoding/gob
不传输类型定义,因此如果另一部分具有不同版本的类型,该过程也可能失败。这记录在包文档中:
不检查接口类型的兼容性;为了传输,所有接口类型都被视为单个“接口”类型的成员,类似于 int 或 []byte - 实际上它们都被视为接口{}。接口值以字符串的形式传输,该字符串标识正在发送的具体类型(必须通过调用 Register 预先定义的名称),后跟后面数据长度的字节数(因此如果不能跳过,则可以跳过该值)存储),然后是存储在接口值中的具体(动态)值的通常编码。(nil 接口值由空字符串标识,不传输任何值。)解码器收到后验证解包的具体项是否满足接收变量的接口。
此外,为了使encoding/gob
包能够根据其名称实例化类型,您必须注册这些类型,更具体地说是这些类型的值。
这是一个使用的工作示例encoding/gob
:
首先让我们改进Calculate()
方法以查看它们被调用:
func (m *method1) Calculcate() {
fmt.Println("method1.Calculate() called, input:", m.Input)
}
func (m *method2) Calculcate() {
fmt.Println("method2.Calculate() called, input:", m.Input)
}
现在是序列化/反序列化过程:
// Register the values we use for the Method interface
gob.Register(&method1{})
gob.Register(&method2{})
model := Calculator{
Method: NewMethod1("inputData"),
}
model.Method.Calculcate()
buf := &bytes.Buffer{}
enc := gob.NewEncoder(buf)
if err := enc.Encode(model); err != nil {
log.Println(err)
return
}
fmt.Println(buf.Bytes())
var model2 Calculator
dec := gob.NewDecoder(buf)
if err := dec.Decode(&model2); err != nil {
log.Println(err)
return
}
model2.Method.Calculcate()
这将输出(在Go Playground上尝试):
method1.Calculate() called, input: inputData
[35 255 129 3 1 1 10 67 97 108 99 117 108 97 116 111 114 1 255 130 0 1 1 1 6 77 101 116 104 111 100 1 16 0 0 0 48 255 130 1 13 42 109 97 105 110 46 109 101 116 104 111 100 49 255 131 3 1 1 7 109 101 116 104 111 100 49 1 255 132 0 1 1 1 5 73 110 112 117 116 1 12 0 0 0 16 255 132 12 1 9 105 110 112 117 116 68 97 116 97 0 0]
method1.Calculate() called, input: inputData
推荐阅读
- python - python 不支持的浮点和整数操作数数据类型
- google-cloud-platform - Google Cloud Platform 上的 GPU 实例在我终止后是否会得到维护?
- python - 由于heroku数据库刷新(django后端)而丢失用户输入数据
- javascript - 在 vuejs、vuex 中获取 406(不可接受的错误)
- javascript - 处理/计算两个对象值并从中生成输出
- c++ - 面临进程退出并返回值 3221225725 的问题
- authentication - 我应该如何为 Electron 桌面应用程序实现用户身份验证/角色?
- html - 网页尺寸太宽
- java - 不关闭 PrintWriter 对象的后果
- c++ - 是否可以组合部分模板特化来生成隐式生成的共享代码路径?