與encoding/json
包不同,您沒有Unmarshaller
接口。在你的情況下,你必須按照你自己的建議使用Decoder
。
下面是一個有效的解決方案:
package main
import (
"bytes"
"encoding/xml"
"fmt"
)
// An interface required by any instruction
type Executer interface {
Execute() error
}
var factoryMap map[string]func() Executer = make(map[string]func() Executer)
type Play struct {
Loops int `xml:"loops,attr"`
File string `xml:",innerxml"`
// Body of element is file name
}
func (p *Play) Execute() error {
for i := 0; i < p.Loops; i++ {
fmt.Println(`o/ ` + p.File)
}
return nil
}
type Say struct {
Voice string `xml:",innerxml"`
}
func (s *Say) Execute() error {
fmt.Println(s.Voice)
return nil
}
// Let's register the different instructions
// You can have each Instruction struct in separate files, letting each file having an init
func init() {
factoryMap["Play"] = func() Executer { return new(Play) }
factoryMap["Say"] = func() Executer { return new(Say) }
}
func Unmarshal(b []byte) ([]Executer, error) {
d := xml.NewDecoder(bytes.NewReader(b))
var actions []Executer
// Finding the first Root tag
for {
v, err := d.Token()
if err != nil {
return nil, err
}
if _, ok := v.(xml.StartElement); ok {
break
}
}
// Looping through the rest of the tokens
// finding the start of each.
for {
v, err := d.Token()
if err != nil {
return nil, err
}
switch t := v.(type) {
case xml.StartElement:
// We found a start of an instruction.
// Let's check the name in our factoryMap
// You should check that the Instruction name actually exists. Now it panics.
f := factoryMap[t.Name.Local]
instr := f()
// We decode the rest of the tag into the instruction struct
err := d.DecodeElement(instr, &t)
if err != nil {
return nil, err
}
// Appending the populated action
actions = append(actions, instr)
case xml.EndElement:
// We found the end tag of the Root. We are done!
return actions, nil
}
}
return nil, nil
}
func main() {
xml := []byte(`<Root>
<Say>Playing file</Say>
<Play loops="2">https://host/somefile.mp3</Play>
<Say>Done playing</Say>
</Root>`)
actions, err := Unmarshal(xml)
if err != nil {
panic(err)
}
for _, instruction := range actions {
err = instruction.Execute()
if err != nil {
fmt.Println(err)
}
}
}
輸出:
Playing file
o/ https://host/somefile.mp3
o/ https://host/somefile.mp3
Done playing
Playground
當然,這個代碼是不完整的,但它應該足夠給你一個關於如何解決問題的清晰畫面。
絕對完美。這裏有50個大你:) –
整潔!哦,我注意到一個錯誤,我通過測試'v == nil'來檢查EOF是否已經達到。這是錯誤的。我應該測試'err == io.EOF'。我刪除了代碼,以免混淆。 – ANisus
我應該不檢查'EOF'嗎?還是應該檢查'err = io.EOF'是否有強健的代碼? –