Skip to content

Go 言語 encode エンコード・デコード

現在のインターネット時代において、最も一般的に使用される言語に依存しないデータ形式には xmlYamljsonprotobuf があります。Go も同様にこれらのデータ形式の関連操作をサポートしています。以下は比較表です。

名称XMLYAMLJSONProtocol Buffers
データ構造複雑やや単純単純やや複雑
保存方式テキストテキストテキストバイナリ
保存サイズ
解析効率遅い速い
言語サポート非常に多い多い非常に多い较多
開発難易度煩雑やや単純単純単純
学習コスト低い低い低い低い
適用範囲データ交換設定ファイルデータ交換データ交換

TIP

Go で構造体のシリアライゼーションとデシリアライゼーションを行うには、フィールドは外部に公開されている必要があります。つまり、最初の文字を大文字にする必要があります。

また、TOML も徐々に普及し始めています。文法は .ini の改良版のようです。興味がある方は TOML:Tom's Obvious, Minimal Language をご覧ください。

XML

xmleXtensible Markup Language の略で、データを保存するための形式です。20 世紀 60 年代に起源を持ち、上記のデータ形式の中で最も古いです。用途は非常に広く、ネットワーク転送、データ交換、設定ファイル、データストレージなどに使用できます。しかし、時代の移り変わりとともに、徐々に新しいマークアップ言語に置き換えられつつあります。

まず構造体を定義します。

go
type Person struct {
   UserId   string `xml:"id"`
   Username string `xml:"name"`
   Age      int    `xml:"age"`
   Address  string `xml:"address"`
}
go
func Marshal(v any) ([]byte, error) //xml シリアライゼーション

func MarshalIndent(v any, prefix, indent string) ([]byte, error) //フォーマット

func Unmarshal(data []byte, v any) error //デシリアライゼーション

シリアライゼーション

go
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))
}

出力

xml
<Person>
        <id>120</id>
        <name>jack</name>
        <age>18</age>
        <address>usa</address>
</Person>

デシリアライゼーション

go
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
   }
}

出力

go
{UserId:120 Username:jack Age:18 Address:usa}

ただし、従来の xml 解析方式は構造体を新規作成する必要があることが多く、非常に煩雑です。現在解析するのは単純な xml 構造ですが、複雑な構造を使用すると非常に面倒です。そのため、サードパーティ製オープンソースライブラリ etree を使用して xml を解析することが多いです。興味がある方は自行で調べてください:Go で使いやすい xml ファイル解析プラグイン etree - 掘金 (juejin.cn)

YML

YAML の構文は他の高級言語と似ており、リスト、ハッシュテーブル、スカラーなどのデータ形式を簡単に表現できます。空白文字のインデントと外観に大きく依存する特徴を使用し、データ構造や各種設定ファイルの表現や編集に特に適しています。YML は多くのプロジェクトで設定ファイルの形式として存在し、コンテンツ構造はより簡潔で一目瞭然です。Go 公式は YML に対するサポートを提供していないため、サードパーティ製パッケージを使用する必要があります。

powershell
go get github.com/go-yaml/yaml

主要メソッド

go
func Marshal(in interface{}) (out []byte, err error) //シリアライゼーション

func Unmarshal(in []byte, out interface{}) (err error) //デシリアライゼーション

構造体を準備します。

go
type Config struct {
   Database string `yaml:"database"`
   Url      string `yaml:"url"`
   Port     int    `yaml:"port"`
   Username string `yaml:"username"`
   Password string `yaml:"password"`
}

設定ファイル

yaml
database: mysql
url: 127.0.0.1
port: 3306
username: root
password: 123456

シリアライゼーション

go
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))
}

出力

yaml
database: oracle
url: localhost
port: 3326
username: root
password: "123456"

yml 自体に厳格なインデント構文があるため、シリアライゼーションフォーマットの問題もありません。

デシリアライゼーション

go
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

jsonRestful スタイルのインターフェース通信でよく使用されます。xml と比較してより軽量で、学習コストが低いため、web 分野で主流のデータ交換形式となっています。

Go では、encoding/json パッケージが json のシリアライゼーションとデシリアライゼーションを行うための関数を提供しています。主に使用する関数は以下の通りです。

go
func Marshal(v any) ([]byte, error) //Go オブジェクトを json 文字列にシリアライズ

func Unmarshal(data []byte, v any) error //json 文字列を Go オブジェクトにデシリアライズ

構造体を定義します。

go
type Person struct {
   UserId   string
   Username string
   Age      int
   Address  string
}

シリアライゼーション

go
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))
}

結果

json
{ "UserId": "120", "Username": "jack", "Age": 18, "Address": "usa" }

フィールド名の再定義

構造体タグを使用して名前を変更できます。

go
type Person struct {
   UserId   string `json:"id"`
   Username string `json:"name"`
   Age      int    `json:"age"`
   Address  string `json:"address"`
}

この時の出力

json
{ "id": "1202", "name": "jack", "age": 19, "address": "USA" }

インデント

シリアライゼーション時はデフォルトでインデントがありません。これは転送過程でのスペースの消費を減らすためですが、人間が観察するには不利です。一部の状況では、人間が観察できる形式にシリアライズする必要があります。そのためには、関数を変更するだけです。

go
func MarshalIndent(v any, prefix, indent string) ([]byte, error)
go
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))
}

出力は以下の通りです。

json
{
  "id": "1202",
  "name": "jack",
  "age": 19,
  "address": "USA"
}

デシリアライゼーション

デシリアライゼーション時は、構造体に json タグがある場合、フィールド名は優先的に json タグを基準とし、そうでない場合は構造体プロパティ名を基準とすることに注意してください。

go
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)
}

出力

go
{UserId:120 Username:jack Age:18 Address:usa}

Protocol Buffers

Protocol Buffers は Google が 2008 年にオープンソース化した、言語中立、プロトコル中立、拡張可能な構造化データシリアライゼーションメカニズムです。上記の 3 つと比較してより軽量で、アンパケット化とパケット化の際により高速です。主に RPC 分野の通信などに使用されます。Protobuf に関する詳細は Protobuf をご覧ください。

依存関係をインストールします。

go get github.com/golang/protobuf/proto

person.proto ファイル

protobuf
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;
}

ファイル生成後

go
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 ファイルに基づいて対応する言語のソースコードを生成します。

Golang学习网由www.golangdev.cn整理维护