はじめに
前回の記事の続きです。
前回の記事はこちら
テキスト処理
今回はテキスト処理についてやっていきます。
XMLの処理
今回は XML
の処理についてですね。RSS
とかでも使われていて、幅広いところで使用されているイメージがあります。
XMLの規約に関する内容には触れず(もし関連した知識が必要であれば他の文献をあたってください)、
どのようにGo言語でXMLファイルをエンコード/デコードするかといった知識についてご紹介します。
<?xml version="1.0" encoding="utf-8"?>
<servers version="1">
<server>
<serverName>Shanghai_VPN</serverName>
<serverIP>127.0.0.1</serverIP>
</server>
<server>
<serverName>Beijing_VPN</serverName>
<serverIP>127.0.0.2</serverIP>
</server>
</servers>
このような XML
の設定ファイルを解析する手順について説明してくれるようです。
XMLの解析
どのようにして上のXMLファイルを解析するのでしょうか?xmlパッケージのUnmarshal関数を使って目的を達成することができます。
Unmershal
というのがあるのですね。
func Unmarshal(data []byte, v interface{}) error
コード例があるので早速プログラムを書いてみましょう。
環境はいつもので!
servers.xml
と xml.go
を作成しました。
下記のようになります。(コードは同じです)
ここからはプログラムの説明ですね、見ていきます。
上の例では、xmlファイルを解析して対応するstructオブジェクトにするにはxml.Unmarshalによって行われました。
この過程はどのように実現されているのでしょうか?我々のstruct定義の後の方を見てみるとxml:“serverName"のような内容があることがわかります。
これはstructの特徴の一つです。struct tagと呼ばれています。これはリフレクションを補助するために用いられます。Unmarshalの定義を見てみましょう
func Unmarshal(data []byte, v interface{}) error
関数には2つの引数があることがわかります。はじめの引数はXMLデータストリームです。ふたつめは保存される対応した型です。現在struct、sliceおよびstringをサポートしています。
なるほど?
今回は、
type Recurlyservers struct {
XMLName xml.Name `xml:"servers"`
Version string `xml:"version,attr"`
Svs []server `xml:"server"`
Description string `xml:",innerxml"`
}
type server struct {
XMLName xml.Name `xml:"server"`
ServerName string `xml:"serverName"`
ServerIP string `xml:"serverIP"`
}
v := Recurlyservers{}
err = xml.Unmarshal(data, &v)
として、解析しているので、 struct
型を引数にしているわけですね。XML
を解析する場合は、タグの構造を構造体で表現して上げる必要があるわけですね。
XMLパッケージの内部ではリフレクションを採用してデータのリフレクションを行なっています。
そのため、vの中のフィールドは必ずエクスポートされなければなりません。
Unmarshalが解析する際XML要素とフィールドはどのように対応づけられるのでしょうか?
これは優先度のあるロードプロセスです。
まずstruct tagを読み込み、もしなければ、対応するフィールド名となります。
注意しなければならないのは、tag、フィールド名、XML要素を解析する際大文字と小文字を区別するということです。そのため、フィールドは逐一対応していなければなりません。
記載ありました、フィールドは対応している必要があるわけですね。
XML
を struct
に解析する際のルールが書いてあります。
ここは読んでおきます。
(ここではじめて、structフィールドのtagの定義
というものを知りました!)
reflect
パッケージで使用するために 定義するものなのですね。
XMLの出力
今度は解析ではなく、XMLの出力についてですね。
xmlパッケージで提供されるMarshalとMarshalIndentという2つの関数で我々の需要を満たすことができます
func Marshal(v interface{}) ([]byte, error)
func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)
今回も、 interface{}
型ですね。
コード例があるので写経します。
おぉ~。XMLが出力できています。
構造体に値を入れて、それを出力したみたいな感じですね。
我々が以前定義したファイルの形式とまったく同じです。
os.Stdout.Write([]byte(xml.Header))というコードが出現したのは、xml.MarshalIndentまたはxml.Marshalが出力する情報がどちらもXMLヘッダを持たないためです。
正しいxmlファイルを生成するために、xmlパッケージであらかじめ定義されているHeader変数を使用しました。
Marshal関数が受け取る引数vはinterface{}型です。つまり任意の型の引数を受け取れることを示しています。
なるほど?
XML
のアウトプットする過程のが記載されていますので、ここも読んでおきましょう。
おわりに
XML
といえば 業務で API
通信するときに使う形式がXML
でしんどかった記憶がある。
しんどい