Go 언어 encode 인코딩 디코딩
현재 인터넷 시대에서 가장 일반적으로 사용되는 언어 독립적인 데이터 포맷은 xml, Yaml, json, protobuf가 있으며, Go 도 이러한 데이터 포맷 관련 조작을 지원합니다. 아래는 비교 표입니다.
| 이름 | XML | YAML | JSON | Protocol Buffers |
|---|---|---|---|---|
| 데이터 구조 | 복잡 | 비교적 단순 | 단순 | 비교적 복잡 |
| 저장 방식 | 텍스트 | 텍스트 | 텍스트 | 바이너리 |
| 저장 크기 | 큼 | 중간 | 중간 | 작음 |
| 해석 효율 | 느림 | 중간 | 중간 | 빠름 |
| 언어 지원 | 매우 많음 | 많음 | 매우 많음 | 비교적 많음 |
| 개발 난이도 | 번거로움 | 비교적 단순 | 단순 | 단순 |
| 학습 비용 | 낮음 | 낮음 | 낮음 | 낮음 |
| 적용 범위 | 데이터 교환 | 설정 파일 | 데이터 교환 | 데이터 교환 |
TIP
Go 에서 구조체의 직렬화와 역직렬화를 하려면 필드가 반드시 외부에 노출되어야 합니다. 즉, 첫 글자가 대문자여야 합니다.
또한 TOML 도 점차 인기를 얻고 있으며, 문법적으로는 .ini의 개선 버전과 같습니다. 관심이 있다면 TOML: Tom's (의미상) 명확한, (설정) 최소화된 언어 를 참고하시기 바랍니다.
XML
xml는 eXtensible Markup Language의 약자로, 데이터를 저장하는 한 가지 형식이며 20 세기 60 년대에 기원하여 위의 여러 데이터 포맷 중 가장 오래된 것입니다. 용도는 매우 광범위하며 네트워크 전송, 데이터 교환, 설정 파일, 데이터 저장 등에 사용할 수 있습니다. 하지만 시대가 변함에 따라 점차 새로운 마크업 언어로 대체되고 있습니다.
먼저 구조체를 정의합니다.
type Person struct {
UserId string `xml:"id"`
Username string `xml:"name"`
Age int `xml:"age"`
Address string `xml:"address"`
}func Marshal(v any) ([]byte, error) //xml 직렬화
func MarshalIndent(v any, prefix, indent string) ([]byte, error) //포맷팅
func Unmarshal(data []byte, v any) error //역직렬화직렬화
func main() {
person := Person{
UserId: "120",
Username: "jack",
Age: 18,
Address: "usa",
}
bytes, err := xml.MarshalIndent(person, "", "\t")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(bytes))
}출력
<Person>
<id>120</id>
<name>jack</name>
<age>18</age>
<address>usa</address>
</Person>역직렬화
func main() {
var person = Person{
UserId: "",
Username: "",
Age: 0,
Address: "",
}
xmlStr := "<Person> \n <id>120</id> \n <name>jack</name> \n <age>18</age> \n <address>usa</address>\n</Person> "
err := xml.Unmarshal([]byte(xmlStr), &person)
if err != nil {
fmt.Println(err)
return
}
}출력
{UserId:120 Username:jack Age:18 Address:usa}하지만 전통적인 xml 해석 방식은 자주 구조체를 새로 만들어야 하므로 매우 번거롭습니다. 현재는 단순한 xml 구조만 해석하므로 복잡한 구조를 사용하면 매우 골치 아플 수 있습니다. 따라서 대부분 서드파티 오픈소스 라이브러리인 etree 를 사용하여 xml 을 해석합니다. 관심이 있다면 직접 알아보세요: Go 에서 사용하기 좋은 xml 파일 해석 플러그인 etree - 掘金 (juejin.cn).
YML
YAML 의 문법은 다른 고급 언어와 유사하며, 리스트, 해시 테이블, 스칼라 등 데이터 형태를 간단하게 표현할 수 있습니다. 공백 문자 들여쓰기와 외관에 크게 의존하는 특징을 사용하여 데이터 구조, 다양한 설정 파일을 표현하거나 편집하는 데 특히 적합하며, YML 은 많은 프로젝트에서 설정 파일 형태로 존재합니다. 내용 구조가 더욱 간결하여 한눈에 볼 수 있습니다. Go 공식에서는 YML 에 대한 지원을 제공하지 않으므로 서드파티 패키지를 사용해야 합니다.
go get github.com/go-yaml/yaml주요 메서드
func Marshal(in interface{}) (out []byte, err error) //직렬화
func Unmarshal(in []byte, out interface{}) (err error) //역직렬화먼저 구조체를 준비합니다.
type Config struct {
Database string `yaml:"database"`
Url string `yaml:"url"`
Port int `yaml:"port"`
Username string `yaml:"username"`
Password string `yaml:"password"`
}설정 파일
database: mysql
url: 127.0.0.1
port: 3306
username: root
password: 123456직렬화
func main() {
config := Config{
Database: "oracle",
Url: "localhost",
Port: 3326,
Username: "root",
Password: "123456",
}
out, err := yaml.Marshal(config)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(out))
}출력
database: oracle
url: localhost
port: 3326
username: root
password: "123456"yml 자체에 엄격한 들여쓰기 문법이 있으므로 직렬화 포맷팅 문제는 없습니다.
역직렬화
func main() {
bytes, err := os.ReadFile("./src/config.yml")
if err != nil {
fmt.Println(err)
return
}
var config Config
err = yaml.Unmarshal(bytes, &config)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(config)
}출력
{mysql 127.0.0.1 3306 root 123456}JSON
json은 Restful 스타일의 인터페이스 통신에서 자주 사용되며, xml에 비해 더 가볍고 학습 비용이 낮아 web 분야에서 주류 데이터 교환 포맷이 되었습니다.
Go 에서 encoding/json 패키지는 json 의 직렬화와 역직렬화를 위한 함수를 제공합니다. 주로 사용되는 함수는 다음과 같습니다.
func Marshal(v any) ([]byte, error) //Go 객체를 json 문자열로 직렬화
func Unmarshal(data []byte, v any) error //json 문자열을 Go 객체로 역직렬화먼저 구조체를 정의합니다.
type Person struct {
UserId string
Username string
Age int
Address string
}직렬화
func main() {
person := Person{
UserId: "120",
Username: "jack",
Age: 18,
Address: "usa",
}
bytes, err := json.Marshal(person)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(bytes))
}결과
{ "UserId": "120", "Username": "jack", "Age": 18, "Address": "usa" }필드 이름 변경
구조체 태그를 사용하여 이름 변경 효과를 얻을 수 있습니다.
type Person struct {
UserId string `json:"id"`
Username string `json:"name"`
Age int `json:"age"`
Address string `json:"address"`
}이때 출력
{ "id": "1202", "name": "jack", "age": 19, "address": "USA" }들여쓰기
직렬화 시 기본적으로 들여쓰기가 없으며, 이는 전송 과정에서의 공간 손실을 줄이기 위함이지만 사람이 관찰하기에는 불리합니다. 어떤 경우에는 사람이 관찰할 수 있는 형태로 직렬화해야 합니다. 이를 위해 함수만 변경하면 됩니다.
func MarshalIndent(v any, prefix, indent string) ([]byte, error)func main() {
person := Person{
UserId: "1202",
Username: "jack",
Age: 19,
Address: "USA",
}
bytes, err := json.MarshalIndent(person, "", "\t")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(bytes))
}출력은 다음과 같습니다.
{
"id": "1202",
"name": "jack",
"age": 19,
"address": "USA"
}역직렬화
역직렬화 시 구조체에 json 태그가 있으면 필드명은 우선적으로 json 태그를 기준으로 하며, 그렇지 않으면 구조체 속성명을 기준으로 합니다.
func main() {
person := Person{}
jsonStr := "{\"id\":\"120\",\"name\":\"jack\",\"age\":18,\"address\":\"usa\"}\n"
err := json.Unmarshal([]byte(jsonStr), &person)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("%+v", person)
}출력
{UserId:120 Username:jack Age:18 Address:usa}Protocol Buffers
protocol 은 구글이 2008 년에 오픈소스로 공개한 언어 중립적, 프로토콜 중립적, 확장 가능한 구조화된 데이터 직렬화 메커니즘입니다. 위의 세 가지에 비해 더 가볍고 언패키징 및 패키징 시 더 빠르며, 주로 RPC 분야 통신 관련에 사용됩니다. Protobuf에 대한 설명은 Protobuf 를 참고하시기 바랍니다.
의존성 설치
go get github.com/golang/protobuf/protoperson.proto 파일
syntax = "proto3";
option go_package = "./;person";
package proto;
enum Gender{
MAIL = 0;
FE_MAIL = 1;
}
message person {
string name = 1;
int32 age = 2;
Gender gender = 3;
}파일 생성 후
package main
import (
p "GoProject/src/proto"
"fmt"
"github.com/golang/protobuf/proto"
)
func main() {
person := p.Person{
Name: "wyh",
Age: 12,
Gender: p.Gender_FE_MAIL,
}
data, err := proto.Marshal(&person)//직렬화
if err != nil {
fmt.Println(err)
return
}
temp := &p.Person{}
fmt.Println("proto buffer len: ", len(data), "bytes:", data)
err = proto.Unmarshal(data, temp)//역직렬화
if err != nil {
fmt.Println(err)
return
}
fmt.Println(temp)
}출력
proto buffer len: 9 bytes: [10 3 119 121 104 16 12 24 1]
name:"wyh" age:12 gender:FE_MAIL하지만 일반적으로 수동으로 직렬화하지 않으며, protoc 컴파일러는 정의된 proto 파일에 따라 해당 언어의 소스 코드를 생성할 수 있습니다.
