Edited at

golangでルートノードしかないXMLをパースする方法

More than 1 year has passed since last update.


背景

golangで標準の encoding/xml を使ったXMLパースをする方法は数多のHowTo記事が存在しますが、仕事でルートノードしかないXMLに出くわし、これのパースにちょっと悩んだので備忘録として残します。


ルートノードしかないXML

こんなやつです。

<?xml version="1.0" encoding="UTF-8"?>

<root>
example
</root>

自分としては、普通にAPI作ったりデータ作るときにこんな構造にすることはまず無いですが、書き方として許されている以上、赤の他人はこのように書くこともあると思っておかなければなりません。


encoding/xml によるパースの例

package main

import (
"encoding/xml"
"fmt"
)

// User is sample struct
type User struct {
ID string `xml:"id"`
User string `xml:"user"`
}

func main() {
xmlStr := `
<?xml version="1.0" encoding="UTF-8"?>
<root>
<id>5ec8da0a-6e23-4343-b474-ca0bb5c22a51</id>
<user>hoge</user>
</root>
`

data := new(User)
if err := xml.Unmarshal([]byte(xmlStr), data); err != nil {
fmt.Println("XML Unmarshal error: ", err)
return
}
fmt.Println(*data)
}

$ go run main.go

{5ec8da0a-6e23-4343-b474-ca0bb5c22a51 hoge}

大したことはありません。structをfiledにtagを指定しつつ定義して、XML文字列とstructのポインタを指定して xml.Unmarshal するだけです。


ルートノードしかないXMLのパース

ところがルートノードしかない場合、structにどんなfieldを定義すればいいのやらさっぱりでした。

もしかしてと思ってやってみたら出来ちゃった結果が以下なのですが、ようはルートノード内が構造化されていないのだから、指定するポインタもプリセットの型を指定すれば良いということですね。

注意としてはルートノード内全てを文字列として xml.Unmarshal しようとするので、前後に改行とか空白があったらそれ毎格納されます。つまりその場合は int とかにはパースできません。

package main

import (
"encoding/xml"
"fmt"
)

func main() {
xmlStr := `
<?xml version="1.0" encoding="UTF-8"?>
<root>example</root>
`

data := new(string)
if err := xml.Unmarshal([]byte(xmlStr), data); err != nil {
fmt.Println("XML Unmarshal error:", err)
return
}
fmt.Println(*data)
}

$ go run main.go

example