データ交換やAPIの利用などで、YAML,JSON,XMLなどのエンコードとデコード処理は必須だ。そこで、Go言語初学者なので、これらの処理方法を調べてみたメモである。この記事に、間違いや、もっと良い方法があれば、教えてください。
パッケージ共通的機能
XML,JSON,YAMLなどを扱うパッケージは、以下の共通的な機能を持っている。
- XML https://pkg.go.dev/encoding/xml
- JSON https://pkg.go.dev/encoding/json
- YAML https://pkg.go.dev/gopkg.in/yaml.v2
共通的機能 Marshal/Unmarshal
バイト型配列を対象として機能する。
Unmarshalは、XML,JSON,YAMLのバイト配列([]byte)を構造体型へ格納する。
- func Unmarshal(data []byte, v any) error
Marshalは構造体型のデータから、XML,JSON,YAMLのバイト配列([]byte)を返す。
- func Marshal(v any) ([]byte, error)
- func MarshalIndent(v any, prefix, indent string) ([]byte, error)
共通的機能 Encoder/Decoder
ファイルなどのI/Oストリームから読み込む、または、書き込むのと合わせて、変換処理する。
Decoderは、XML,JSON,YAMLなどのファイルを読み込んで、構造体型へ格納する。
- func NewDecoder(r io.Reader) *Decoder
- func (dec *Decoder) Decode(v interface{}) (err error)
Encoderは、構造体型から、XML,JSON,YAMLへ書出す。
- func NewEncoder(w io.Writer) *Encoder
- func (e *Encoder) Encode(v interface{}) (err error)
XMLについて
XMLファイルの読み込みと書き出しについて、Marshal/Unmarshal、Encoder/Decoder の4パターンについて、以下の示す。このパッケージが機能するためには、XML名前フィールドに対応づけたタグを構造体型に追記しなければならない。具体的なタグの書き方は、パッケージのドキュメントを参照願う。
XML Decord (XMLファイルから構造体へ)
package main
import (
"encoding/xml"
"os"
)
func readXml(fn string, xf interface{}) error {
file, err := os.Open(fn)
if err != nil {
return err
}
defer file.Close()
decoder := xml.NewDecoder(file)
err = decoder.Decode(xf) // メモリ上の構造体へ展開
if err != nil {
return err
}
return nil
}
XML Encode (構造体からXMLファイルへ)
XMLファイルにインデントしないと見にくいので、encoder.Indent(""," ")
で設定した。
package main
import (
"encoding/xml"
"os"
)
func writeXml(fn string, xf interface{}) error {
file, err := os.Create(fn)
if err != nil {
return err
}
defer file.Close()
encoder := xml.NewEncoder(file)
encoder.Indent(""," ")
err = encoder.Encode(xf)
if err != nil {
return err
}
return nil
}
XML Unmarshal (メモリ上のXMLから構造体へ)
package main
import (
"encoding/xml"
"io"
"os"
)
func readXml(fn string, xf interface{}) error {
file, err := os.Open(fn)
if err != nil {
return err
}
defer file.Close()
byteValue, err := io.ReadAll(file)
if err != nil {
return err
}
err = xml.Unmarshal(byteValue, xf) // 構造体へ展開
if err != nil {
return err
}
return nil
}
XML Marshal (構造体からメモリ上のXMLへ)
こちらも、人間が目で読みやすい様に、xml.MarshalIndent(xf, "", " ")
でインデントを設定する。
package main
import (
"encoding/xml"
"os"
)
func writeXml(fn string, xf interface{}) error {
file, err := os.Create(fn)
if err != nil {
return err
}
defer file.Close()
output, err := xml.MarshalIndent(xf, "", " ")
if err != nil {
return err
}
file.Write(output)
return nil
}
JSONについて
JSONパッケージは、JSONファイルのキー名と、構造体の要素変数名が一致してれば、タグを記述する必要が無いので便利だ。
JSON Decord (JSONファイルから構造体へ)
複数のJSON行を読み、構造体型の入れるへ格納する。この様な処理では、Interface型が利用できないので、注意しなければならない。
package main
import (
"os"
"io"
"encoding/json"
)
// パラメータ mems の型は、対象に合わせて要変更
func readJson(fn string, mems *[]Member) (error) {
file,err := os.Open(fn)
if err != nil {
return err
}
defer file.Close()
// 行単位に読んで、構造体型の配列へセット
dec := json.NewDecoder(file)
for {
var m Member
if err := dec.Decode(&m); err == io.EOF {
break
} else if err != nil {
return err
}
*mems = append(*mems, m) //配列へ要素追加
}
return nil
}
JSON Encode (構造体からJSONファイルへ)
構造体型を一括でファイルへ書出す。こちらはインタフェース型を利用できる。
package main
import (
"os"
"encoding/json"
)
func writeJson(fn string, jf interface{}) (error) {
file,err := os.Create(fn)
if err != nil {
return err
}
defer file.Close()
encoder := json.NewEncoder(file)
encoder.SetIndent("", " ")
err = encoder.Encode(jf) // ストリームへJSON形式で書き出し
if err != nil {
return err
}
return nil
}
JSON Unmarshal (メモリ上のJSONから構造体へ)
配列形式のJSONデータを一括で読み込み。
package main
import (
"os"
"io"
"encoding/json"
)
func readJson(fn string, jf interface{}) (error) {
file,err := os.Open(fn)
if err != nil {
return err
}
defer file.Close()
byteJSON,err := io.ReadAll(file)
if err != nil {
return err
}
err = json.Unmarshal(byteJSON,jf) // メモリ上のJSONを構造体へ展開
if err != nil {
return err
}
return nil
}
JSON Marshal (構造体からメモリ上のJSONへ)
人が読む必要がある時は、json.MarshalIndent(jf,""," ")
によってインデントを付けて書き込みができる。
package main
import (
"os"
"encoding/json"
)
func writeJson(fn string, jf interface{}) (error) {
file,err := os.Create(fn)
if err != nil {
return err
}
defer file.Close()
byteJSON,err := json.MarshalIndent(jf,""," ")
if err != nil {
return err
}
_,err = file.Write(byteJSON)
if err != nil {
return err
}
return nil
}
YAMLについて
YAMLのパッケージが機能するためには、YAMLのキーに対応づくタグを、構造体へ記述しなければならない。タグの記述については、パッケージのドキュメントを参照していただきたい。
YAML Decord (YAMLファイルから構造体へ)
JSONやXMLと同じようにコードを書ける。また、インタフェース型を利用することで、展開する構造体に依存しないコードになる。
package main
import (
"github.com/go-yaml/yaml"
"os"
)
func readYAML(fn string, yf interface{}) error {
file, err := os.Open(fn)
if err != nil {
return err
}
defer file.Close()
decoder := yaml.NewDecoder(file)
err = decoder.Decode(yf)
if err != nil {
return err
}
return nil
}
YAML Encode (構造体からYAMLファイルへ)
JSONやXMLと同じようにコードを書ける。
package main
import (
"github.com/go-yaml/yaml"
"os"
)
func writeYAML(fn string, yf interface{}) error {
file, err := os.Create(fn)
if err != nil {
return err
}
defer file.Close()
encoder := yaml.NewEncoder(file)
defer encoder.Close()
err = encoder.Encode(yf)
if err != nil {
return err
}
return nil
}
YAML Unmarshal (メモリ上のYAMLから構造体へ)
利用するパッケージ以外に、他と変わりが無い。
package main
import (
"github.com/go-yaml/yaml"
"os"
"io"
)
func readYAML(fn string, yf interface{}) error {
file, err := os.Open(fn)
if err != nil {
return err
}
defer file.Close()
byteData,err := io.ReadAll(file)
if err != nil {
return err
}
err = yaml.Unmarshal(byteData,yf) //メモリ上のYAMLを構造体へ
if err != nil {
return err
}
return nil
}
YAML Marshal (構造体からメモリ上のYAMLへ)
インデントの設定メソッドが無い。YAMLは桁下げが必須なので、読みやすさの点で問題ない。
package main
import (
"github.com/go-yaml/yaml"
"os"
)
func writeYAML(fn string, yf interface{}) error {
file, err := os.Create(fn)
if err != nil {
return err
}
defer file.Close()
byteData,err := yaml.Marshal(yf) //メモリ上にYAMLを書き出し
if err != nil {
return err
}
_,err = file.Write(byteData)
if err != nil {
return err
}
return nil
}
まとめ
現在主要なデータ連携やAPIに利用されるXML,JSON,YAMLについて、Go言語の読書ついて調べて解ったことを、箇条書きにする。
- YAML,XMLのパッケージが機能するためには、構造体へタグの追加が必要。しかし、JSONは名前が一致していれば構造体へセットされる。
- Marshal/Unmarshalは、メモリ上のバイト型配列を対象した操作
- Encoder/Decoderは、ファイルI/Oなど、ストリーム処理を対象とした操作
- データ形式によってパッケージを変えるだけで、ほとんど変更なしで対応できそう
- インタフェース型を利用することで、メモリ上にデータを保持するための構造体に影響されないメソッドが書ける。
参考資料
- Config Files: INI, XML, JSON, YAML, TOML, https://www.barenakedcoder.com/blog/2020/03/config-files-ini-xml-json-yaml-toml/
- XMLパッケージ, https://pkg.go.dev/encoding/xml
- JSONパッケージ, https://pkg.go.dev/encoding/json
- YAMLパッケージ, https://pkg.go.dev/gopkg.in/yaml.v2