はじめに

前回の記事の続きです。
前回の記事はこちら

テキスト処理

今回はテキスト処理についてやっていきます。

XMLの処理

今回は XML の処理についてですね。
RSS とかでも使われていて、幅広いところで使用されているイメージがあります。

XMLの規約に関する内容には触れず(もし関連した知識が必要であれば他の文献をあたってください)、
どのようにGo言語でXMLファイルをエンコード/デコードするかといった知識についてご紹介します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<?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 というのがあるのですね。

1
func Unmarshal(data []byte, v interface{}) error

コード例があるので早速プログラムを書いてみましょう。
環境はいつもので!

servers.xmlxml.goを作成しました。
下記のようになります。(コードは同じです) xml-parse-01

ここからはプログラムの説明ですね、見ていきます。

上の例では、xmlファイルを解析して対応するstructオブジェクトにするにはxml.Unmarshalによって行われました。
この過程はどのように実現されているのでしょうか?我々のstruct定義の後の方を見てみるとxml:“serverName"のような内容があることがわかります。
これはstructの特徴の一つです。struct tagと呼ばれています。これはリフレクションを補助するために用いられます。Unmarshalの定義を見てみましょう

1
func Unmarshal(data []byte, v interface{}) error

関数には2つの引数があることがわかります。はじめの引数はXMLデータストリームです。ふたつめは保存される対応した型です。現在struct、sliceおよびstringをサポートしています。

なるほど?

今回は、

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
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要素を解析する際大文字と小文字を区別するということです。そのため、フィールドは逐一対応していなければなりません。

記載ありました、フィールドは対応している必要があるわけですね。

XMLstruct に解析する際のルールが書いてあります。
ここは読んでおきます。
(ここではじめて、structフィールドのtagの定義 というものを知りました!) reflect パッケージで使用するために 定義するものなのですね。

XMLの出力

今度は解析ではなく、XMLの出力についてですね。

xmlパッケージで提供されるMarshalとMarshalIndentという2つの関数で我々の需要を満たすことができます

1
2
func Marshal(v interface{}) ([]byte, error)
func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)

今回も、 interface{}型ですね。
コード例があるので写経します。

xml-out-01

おぉ~。XMLが出力できています。
構造体に値を入れて、それを出力したみたいな感じですね。

我々が以前定義したファイルの形式とまったく同じです。
os.Stdout.Write([]byte(xml.Header))というコードが出現したのは、xml.MarshalIndentまたはxml.Marshalが出力する情報がどちらもXMLヘッダを持たないためです。
正しいxmlファイルを生成するために、xmlパッケージであらかじめ定義されているHeader変数を使用しました。
Marshal関数が受け取る引数vはinterface{}型です。つまり任意の型の引数を受け取れることを示しています。

なるほど?

XML のアウトプットする過程のが記載されていますので、ここも読んでおきましょう。

おわりに

XML といえば 業務で API通信するときに使う形式がXML でしんどかった記憶がある。
しんどい