概要
よく忘れるので、ここにメモメモ。。。
xml の Marshal/Unmarshal で入れ子の要素の値を取得する場合、ちゃんと要素毎に type 作って指定していくのが正道だと思いますが、面倒なので一気に取得したいときがあります。
んで、 xml パッケージのドキュメントを見ると以下のように記載されています。
https://pkg.go.dev/encoding/xml@go1.18.4
の Unmarshal の部分に
If the XML element contains a sub-element whose name matches the prefix of a tag formatted as "a" or "a>b>c", unmarshal will descend into the XML structure looking for elements with the given names, and will map the innermost elements to that struct field.
XML要素が "a "または "a>b>c "のようなタグの接頭辞と名前が一致するサブ要素を含む場合、 unmarshalはXML構造体に降りていき、与えられた名前を持つ要素を探し、 最も内側の要素をその構造体フィールドにマップします。(By DeepL先生)
xpathみたいな感じですね。Marshalの場合も同様です。
サンプル
package xmlop import ( "encoding/xml" "github.com/devlights/gomy/output" ) // NestedElements -- 入れ子になっている要素を取得するサンプルです。 // // REFERENCES: // - https://pkg.go.dev/encoding/xml@latest#Unmarshal func NestedElements() error { const ( xmlStr = ` <data> <items> <item> <value>hello</value> </item> <item> <value>world</value> </item> </items> </data>` ) type xmldata struct { XMLName xml.Name `xml:"data"` // ルート要素 Values []string `xml:"items>item>value"` // 入れ子の要素 } var ( data xmldata err error ) err = xml.Unmarshal([]byte(xmlStr), &data) if err != nil { return err } for _, v := range data.Values { output.Stdoutf("[result]", "%v\n", v) } return nil }
実行すると以下のようになります。
gitpod /workspace/try-golang (master) $ task run task: [run] go run . -onetime ENTER EXAMPLE NAME: xml_nested_elements [Name] "xml_nested_elements" [result] hello [result] world [Elapsed] 87.31µs
参考情報
https://pkg.go.dev/encoding/xml@go1.18.4#Marshal
https://pkg.go.dev/encoding/xml@go1.18.4#Unmarshal
過去の記事については、以下のページからご参照下さい。
サンプルコードは、以下の場所で公開しています。