>  기사  >  백엔드 개발  >  golang의 직렬화 방법은 무엇입니까?

golang의 직렬화 방법은 무엇입니까?

青灯夜游
青灯夜游원래의
2023-01-04 19:33:284004검색

Golang 직렬화 방법은 다음과 같습니다. 1. Gob 패키지를 사용하여 gob 스트림을 관리합니다. Gob은 유형에 바인딩되어 있으며 더 많거나 적은 것으로 확인되면 순서에 따라 채워지거나 잘립니다. 2. json 패키지를 사용하면 직렬화 프로세스 중에 RFC 7159에 정의된 JSON 인코딩 및 디코딩을 구현할 수 있습니다. 구조의 멤버가 소문자인 경우 오류가 발생합니다. 3. Binary 패키지를 사용하면 숫자와 바이트 시퀀스 간의 간단한 변환과 varint의 인코딩 및 디코딩이 가능합니다. 4. protobuf 프로토콜을 사용하십시오.

golang의 직렬화 방법은 무엇입니까?

이 튜토리얼의 운영 환경: Windows 7 시스템, GO 버전 1.18, Dell G3 컴퓨터.

프로그래밍 과정에서 우리는 항상 데이터 개체를 네트워크를 통해 전송하거나 파일에 저장하는 문제에 직면해야 하며, 이를 위해서는 인코딩 및 디코딩 작업이 필요합니다.

현재 인코딩 형식은 json, XML, Gob, Google 프로토콜 버퍼 등 다양합니다. Go 언어에서는 어떻게 데이터를 이런 방식으로 인코딩하고 디코딩할 수 있나요?

직렬화 및 역직렬화 정의

직렬화(Serialization)는 객체의 상태 정보를 저장하거나 전송할 수 있는 형식으로 변환하는 프로세스입니다. 직렬화 중에 객체는 현재 상태를 임시 또는 영구 저장소에 씁니다.

차례로 저장 영역에서 변수를 다시 읽어서 객체를 다시 생성하는데, 이것이 deserialization입니다.

Go 언어에서 encoding 패키지는 이러한 유형의 직렬화 인코딩 및 디코딩 문제를 처리하도록 특별히 설계되었습니다.

직렬화 모드 - Gob

gob 패키지는 gob 스트림, 즉 인코더(송신자)와 디코더(수신자) 간에 교환되는 바이너리 값을 관리합니다. 일반적인 용도는 "net/rpc" 패키지에 사용되는 gobs 스트림과 같은 원격 프로시저 호출(RPC)의 매개변수와 결과를 전송하는 것입니다. gob 包管理 gob 流–编码器(发送器)和解码器(接收器)之间交换的二进制值。一个典型的用途是传输远程过程调用(RPCs)的参数和结果,如 "net/rpc "包中就使用了gobs 流。

具体可以参考文档:https://docs.studygolang.com/pkg/encoding/gob/

他的官网给出了一个示例:

package main

import (
	"bytes"
	"encoding/gob"
	"fmt"
	"log"
)

type P struct {
	X, Y, Z int
	Name    string
}

type Q struct {
	X, Y *int32
	Name string
}

// This example shows the basic usage of the package: Create an encoder,
// transmit some values, receive them with a decoder.
func main() {
	// Initialize the encoder and decoder. Normally enc and dec would be
	// bound to network connections and the encoder and decoder would
	// run in different processes.
	var network bytes.Buffer        // Stand-in for a network connection  //Buffer是具有Read和Write方法的可变大小的字节缓冲区。
	enc := gob.NewEncoder(&network) // Will write to network.
	dec := gob.NewDecoder(&network) // Will read from network.

	// Encode (send) some values.
	err := enc.Encode(P{3, 4, 5, "Pythagoras"})
	if err != nil {
		log.Fatal("encode error:", err)
	}
	err = enc.Encode(P{1782, 1841, 1922, "Treehouse"})
	if err != nil {
		log.Fatal("encode error:", err)
	}

	// Decode (receive) and print the values.
	var q Q
	err = dec.Decode(&q)
	if err != nil {
		log.Fatal("decode error 1:", err)
	}
	fmt.Printf("%q: {%d, %d}\n", q.Name, *q.X, *q.Y)
	err = dec.Decode(&q)
	if err != nil {
		log.Fatal("decode error 2:", err)
	}
	fmt.Printf("%q: {%d, %d}\n", q.Name, *q.X, *q.Y)

}

运行结果是:

"Pythagoras": {3, 4}
"Treehouse": {1782, 1841}

个人认为这个例子是真的好。我们看到,结构体PQ 是不同的,我们看到Q 少了一个 Z 变量。

但是,在解码的时候,仍然能解析得出来,这说明,使用 gob 时,是根据类型绑定的,如果发现多了或者少了,会依据顺序填充或者截断。

接下来,我们详情说说怎么编码吧:

1. bytes.Buffer 类型

首先,我们需要定义一个 bytes.Buffer 类型,用来承接需要序列化的结构体,这个类型是这样的:

// A Buffer is a variable-sized buffer of bytes with Read and Write methods.(Buffer是具有Read和Write方法的可变大小的字节缓冲区)
// The zero value for Buffer is an empty buffer ready to use.
type Buffer struct {
	buf      []byte // contents are the bytes buf[off : len(buf)]
	off      int    // read at &buf[off], write at &buf[len(buf)]
	lastRead readOp // last read operation, so that Unread* can work correctly.
}

使用上面的例子,可以看到输出是:

"Pythagoras": {3, 4} ==>
{[42 255 129 3 1 1 1 80 1 255 130 0 1 4 1 1 88 1 4 0 1 1 89 1 4 0 1 1 90 1 4 0 1 4 78 97 109 101 1 12 0 0 0 21 255 130 1 6 1 8 1 10 1 10 80 121 116 104 97 103 111 114 97 115 0] 0 0}

可以看到,Buffer 里,是二进制数(一个字节8个bit,最高255)

2. Encode 编码

之后,对需要编码序列化的结构体进行编码:

enc := gob.NewEncoder(&network) // Will write to network.
// Encode (send) some values.
if err := enc.Encode(P{3, 4, 5, "Pythagoras"}); err != nil {
	log.Fatal("encode error:", err)
}

这里,首先是要获得 *Encoder 对象,获得对象后,利用 *Encoder 对象的方法 Encode 进行编码。

这里,需要注意的是,Encode 如果是网络编程的,其实是可以直接发送消息给对方的,而不必进行 socket 的send 操作。

比如:在 srever 端有代码:

func main() {
	l, err := net.Listen("tcp", "127.0.0.1:8000")  //监听端口
	if err != nil {
		log.Fatal("net Listen() error is ", err)
	}

	p := P{
		1, 2, 3,
		"name"}

	conn, err := l.Accept()
	if err != nil {
		log.Fatal("net Accept() error is ", err)
	}
	defer func() { _ = conn.Close() }()
	//参数是conn 时,即可发出
	enc := gob.NewEncoder(conn)
	if err = enc.Encode(p); err != nil {  //发生结构体数据
		log.Fatal("enc Encode() error is ", err)
	}
}

在客户端client有:

func main() {
	conn,err := net.Dial("tcp","127.0.0.1:8000")
	if err != nil {
		log.Fatal("net Dial() error is ", err)
	}
	defer func() { _ = conn.Close() }()
	/**
	type Q struct {
		X, Y int
		Name string
	}
	 */
	var q Q
	dec := gob.NewDecoder(conn)
	if err = dec.Decode(&q); err != nil {
		log.Fatal("enc Encode() error is ", err)
	}
	fmt.Println(q)
}

输出:

{1 2 name}

3. Decode 解码

最后,对其解码的步骤为:

dec := gob.NewDecoder(&network) // Will read from network.
if err = dec.Decode(&q);err != nil {
	log.Fatal("decode error 2:", err)
}

序列化方式–json

json 包实现了 RFC 7159 中定义的 JSON 编码和解码。JSON和Go值之间的映射在 Marshal 和 Unmarshal 函数的文档中进行了描述。

有关此程序包的介绍,请参见“ JSON和Go”:https://www.php.cn/link/241200d15bc67211b50bd10815259e58json/

示例如下:

type Message struct {
	QQ      string
	Address string
}

type Student struct {
	Id   uint64 `json:"id"` //可以保证json字段按照规定的字段转义,而不是输出 Id
	Age  uint64 `json:"age"`
	Data []Message
}

func main() {
	m1 := Message{QQ: "123", Address: "beijing"}
	m2 := Message{QQ: "456", Address: "beijing"}
	s1 := Student{3, 19, append([]Message{}, m1, m2)}
	var buf []byte
	var err error

	if buf, err = json.Marshal(s1); err != nil {
		log.Fatal("json marshal error:", err)
	}

	fmt.Println(string(buf))

	var s2 Student
	if err = json.Unmarshal(buf, &s2); err != nil {
		log.Fatal("json unmarshal error:", err)
	}
	fmt.Println(s2)
}
//输出:
//{"id":3,"age":19,"Data":[{"QQ":"123","Address":"beijing"},{"QQ":"456","Address":"beijing"}]}
//{3 19 [{123 beijing} {456 beijing}]}

注意

在序列化的过程中,如果结构体内的成员是小写的,则会出现错误。以上两种方式,都会出现这样的结果

我们以 json

자세한 내용은 문서를 참조하세요: https://docs.studygolang.com/pkg/encoding/gob/

그의 공식 웹사이트는 예를 제공합니다:

package main

import (
	"encoding/json"
	"fmt"
	"log"
)

type Message struct {
	qq      string
	address string
}

type Student struct {
	Id   uint64 `json:"id"` //可以保证json字段按照规定的字段转义,而不是输出 Id
	Age  uint64 `json:"age"`
	Data []Message
}

func main() {
	m1 := Message{"123", "beijing"}
	m2 := Message{"456", "beijing"}
	s1 := Student{3, 19, append([]Message{}, m1, m2)}
	var buf []byte
	var err error

	if buf, err = json.Marshal(s1); err != nil {
		log.Fatal("json marshal error:", err)
	}

	fmt.Println(string(buf))

	var s2 Student
	if err = json.Unmarshal(buf, &s2); err != nil {
		log.Fatal("json unmarshal error:", err)
	}
	fmt.Println(s2)
}

실행 결과는:

{"id":3,"age":19,"Data":[{},{}]}
{3 19 [{ } { }]}

개인적으로 저는 이 예가 사실이라고 생각하세요. 좋아요. PQ 구조가 다르다는 것을 알 수 있습니다. Q에는 Z 변수가 없습니다.

그러나 디코딩 중에도 구문 분석이 가능합니다. 이는 gob를 사용할 때 유형에 따라 바인딩됨을 보여줍니다. 순서에 따라 잘립니다. 다음으로 인코딩 방법을 자세히 살펴보겠습니다.

1. bytes.Buffer type

먼저 bytes.Buffer 유형은 직렬화해야 하는 구조를 받아들이는 데 사용됩니다. 이 유형은 다음과 같습니다.

type Message struct {
	qq      string
	address string
}

type Student struct {
	Id   uint64 `json:"id"` //可以保证json字段按照规定的字段转义,而不是输出 Id
	Age  uint64 `json:"age"`
	Data []Message
}

func main() {
	m1 := Message{"123", "beijing"}
	m2 := Message{"456", "beijing"}
	s1 := Student{3, 19, append([]Message{}, m1, m2)}

	var buf bytes.Buffer
	enc := gob.NewEncoder(&buf)
	if err := enc.Encode(s1); err != nil {
		log.Fatal("encode error:", err) //报错
	}
	fmt.Println(string(buf.Bytes()))
}
위의 예를 사용하면 출력이 다음과 같은 것을 볼 수 있습니다.

2020/12/30 16:44:47 encode error:gob: type main.Message has no exported fields
🎜볼 수 있습니다. 버퍼 는 이진수입니다(바이트당 8비트, 최대 255)🎜🎜
🎜🎜2. 인코딩 인코딩 후🎜🎜🎜에서 인코딩 및 직렬화를 수행합니다. 구조체 인코딩: 🎜
package main

import (
	"bytes"
	"encoding/binary"
	"fmt"
)

func main() {
	buf := new(bytes.Buffer)
	var pi int64 = 255

	err := binary.Write(buf, binary.LittleEndian, pi)
	if err != nil {
		fmt.Println("binary.Write failed:", err)
	}
	fmt.Println( buf.Bytes())
}
//输出:
[255 0 0 0 0 0 0 0]
🎜여기서 먼저 *Encoder 객체를 구한 후 *EncoderEncode 메소드를 사용하세요. /code> 인코딩할 개체입니다. 🎜🎜🎜여기서, 네트워크 프로그래밍에 인코드를 사용하면 소켓 전송 작업을 수행하지 않고도 실제로 상대방에게 직접 메시지를 보낼 수 있다는 점에 유의해야 합니다. 🎜🎜🎜예: srever 측에 코드가 있습니다: 🎜
binary.Write failed: binary.Write: invalid type int
🎜클라이언트 측에: 🎜rrreee🎜출력: 🎜rrreee🎜
🎜🎜3. decoder🎜 🎜🎜마지막으로 디코딩하는 단계는 다음과 같습니다. 🎜rrreee🎜🎜🎜직렬화 방법 – json🎜🎜🎜json 패키지는 RFC 7159를 구현합니다. <code>JSON 인코딩 및 디코딩은 code>에 정의되어 있습니다. JSON과 Go 값 간의 매핑은 Marshal 및 Unmarshal 함수에 대한 설명서에 설명되어 있습니다. 🎜🎜이 패키지에 대한 소개는 "JSON and Go"를 참조하세요: https://www.php.cn/link/241200d15bc67211b50bd10815259e58json/🎜🎜예는 다음과 같습니다: 🎜rrreee

🎜 🎜Note🎜

🎜직렬화 과정에서 구조체의 멤버가 소문자일 경우 오류가 발생합니다. 🎜위의 두 가지 방법은 이러한 결과를 생성합니다.🎜🎜🎜json 직렬화를 예로 들어 소문자인 경우 결과가 어떻게 될지 확인합니다. 🎜rrreee🎜출력: 🎜rrreee 🎜우리는 소문자 부분은 직렬화되지 않습니다. 즉, null 값이 됩니다. 🎜🎜오류를 보고하지는 않지만 분명히 우리가 보고 싶은 결과는 아닙니다. 🎜🎜🎜🎜🎜오류: gob: xxx 유형에는 내보낸 필드가 없습니다.🎜🎜🎜오류를 보고하는 예를 살펴보겠습니다.🎜rrreee🎜이 코드는 오류를 보고합니다.🎜rrreee🎜🎜대문자 사용을 기억하세요. 구조가 매우 민감합니다! ! ! 🎜🎜

序列化方式–Binary

Binary 包实现 数字字节 序列之间的简单转换以及varint的编码和解码。

通过读取和写入固定大小的值来转换数字。 固定大小的值可以是固定大小的算术类型(bool,int8,uint8,int16,float32,complex64等),也可以是仅包含固定大小值的数组或结构体。详情可参考:https://www.php.cn/link/241200d15bc67211b50bd10815259e58binary/#Write

示例:

package main

import (
	"bytes"
	"encoding/binary"
	"fmt"
)

func main() {
	buf := new(bytes.Buffer)
	var pi int64 = 255

	err := binary.Write(buf, binary.LittleEndian, pi)
	if err != nil {
		fmt.Println("binary.Write failed:", err)
	}
	fmt.Println( buf.Bytes())
}
//输出:
[255 0 0 0 0 0 0 0]

这里需要注意:如果序列化的类型是 int 类型的话,将会报错:

binary.Write failed: binary.Write: invalid type int

而且,序列化的值是空的。

这是由于,他在前面已经解释清楚了,只能序列化固定大小的类型(bool,int8,uint8,int16,float32,complex64…),或者是结构体和固定大小的数组。

其他序列化方法

当然,go语言还有其他的序列化方法,如 protobuf 协议,参考:https://geektutu.com/post/quick-go-protobuf.html

【相关推荐:Go视频教程编程教学

위 내용은 golang의 직렬화 방법은 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.