LoginSignup
2
3

More than 1 year has passed since last update.

XML,JSON,YAML のGo言語による操作について調べた結果

Posted at

データ交換やAPIの利用などで、YAML,JSON,XMLなどのエンコードとデコード処理は必須だ。そこで、Go言語初学者なので、これらの処理方法を調べてみたメモである。この記事に、間違いや、もっと良い方法があれば、教えてください。

パッケージ共通的機能

XML,JSON,YAMLなどを扱うパッケージは、以下の共通的な機能を持っている。

共通的機能 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など、ストリーム処理を対象とした操作
  • データ形式によってパッケージを変えるだけで、ほとんど変更なしで対応できそう
  • インタフェース型を利用することで、メモリ上にデータを保持するための構造体に影響されないメソッドが書ける。

参考資料

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3