GoでWeb APIのXMLレスポンスのテストを書いているとき、「不要なXML要素が返却されていないか」というテスト項目が必要になりました。普通にGoのxml.Unmarshalを使っていると、構造体に未定義な要素は単に無視されてしまうので、工夫が必要です。
// 余計な要素(XXX)が無視されてしまう例
type Person struct {
Name string
Gender string
Age int
}
badxml := []byte(`
<Person>
<Name>John</Name>
<Gender>Male</Gender>
<Age>20</Age>
<XXX>xxxxxxxxxxx</XXX>
</Person>`)
p := Person{}
if err := xml.Unmarshal(badxml, &p); err != nil {
log.Fatal(err)
}
fmt.Println(p.Age) // -> 20
encoding/xml
パッケージで、,any
というフラグが定義されています。
* If the XML element contains a sub-element that hasn't matched any
of the above rules and the struct has a field with tag ",any",
unmarshal maps the sub-element to that struct field.
XML要素の中で、他のルールに適合しないサブ要素があるとき、",any"タグのついた構造体フィールドにマッピングされるそうです。これで余分なXML要素名を得る事が出来ます。
せっかくなので、これを応用して「XMLデコード中に余計な要素が現れたらエラーを返す型」を作ってみました。
type NoExtra struct {
I noExtraInner `xml:",any"`
}
type noExtraInner struct{}
func (u *noExtraInner) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
return xml.UnmarshalError(fmt.Sprint("detected an extra XML element. (", start.Name.Space, start.Name.Local, ")"))
}
このNoExtra型を下のように構造体フィールドに含めることで、Name,Age,Gender以外のXMLサブ要素が含まれている場合にxml.Unmarshal
関数がエラーを返すことができます
Demo: http://play.golang.org/p/lJaBwq0Yry
type Person struct {
Name string
Gender string
Age int
NoExtra
}
badxml := []byte(`
<Person>
<Name>John</Name>
<Gender>Male</Gender>
<Age>20</Age>
<XXX>xxx</XXX>
</Person>`)
p := Person{}
if err := xml.Unmarshal(badxml, &p); err != nil {
log.Fatal(err) // -> detected an extra XML element. (XXX)
}
こちらからは以上です。